import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../bloc/book/book_bloc.dart'; import '../bloc/book/book_state.dart'; import '../bloc/library/library_bloc.dart'; import '../bloc/library/library_event.dart'; import '../bloc/library/library_state.dart'; import '../widgets/book_card.dart'; import '../theme/app_spacing.dart'; import 'book_details_screen.dart'; import 'add_book_screen.dart'; class LibraryScreen extends StatelessWidget { const LibraryScreen({super.key}); @override Widget build(BuildContext context) { return BlocProvider( create: (context) => LibraryBloc(), child: const _LibraryScreenContent(), ); } } class _LibraryScreenContent extends StatelessWidget { const _LibraryScreenContent(); @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; final textTheme = Theme.of(context).textTheme; return BlocBuilder( builder: (context, libraryState) { return BlocBuilder( builder: (context, bookState) { final filtered = bookState.books.where((b) { final q = libraryState.searchQuery.toLowerCase(); return b.title.toLowerCase().contains(q) || b.author.toLowerCase().contains(q); }).toList(); return Stack( children: [ SafeArea( child: Column( children: [ // Header Padding( padding: const EdgeInsets.fromLTRB( AppSpacing.lg, AppSpacing.md, AppSpacing.lg, 0, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Книжная полка', style: textTheme.displayMedium, ), IconButton( icon: const Icon(Icons.notifications_outlined), onPressed: () {}, ), ], ), ), // Search Padding( padding: const EdgeInsets.fromLTRB( AppSpacing.lg, AppSpacing.md, AppSpacing.lg, 0, ), child: TextField( onChanged: (v) { context.read().add( UpdateSearchQuery(v), ); }, decoration: InputDecoration( hintText: 'Поиск книг...', prefixIcon: Icon( Icons.search, color: colorScheme.primary, ), ), ), ), // Tabs Padding( padding: const EdgeInsets.fromLTRB( AppSpacing.lg, AppSpacing.md, AppSpacing.lg, 0, ), child: Row( children: [ _tab( context, 'Все книги', 0, libraryState.tabIndex, ), const SizedBox(width: AppSpacing.sm), _tab( context, 'Категории', 1, libraryState.tabIndex, ), ], ), ), const SizedBox(height: AppSpacing.md), // Grid Expanded( child: libraryState.tabIndex == 0 ? GridView.builder( padding: const EdgeInsets.fromLTRB( AppSpacing.lg, 0, AppSpacing.lg, 100, ), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, childAspectRatio: 0.55, crossAxisSpacing: AppSpacing.md, mainAxisSpacing: AppSpacing.md, ), itemCount: filtered.length, itemBuilder: (context, i) => BookCard( book: filtered[i], onTap: () { Navigator.of(context).push( MaterialPageRoute( builder: (_) => BookDetailsScreen( book: filtered[i], ), ), ); }, ), ) : ListView( padding: const EdgeInsets.fromLTRB( AppSpacing.lg, 0, AppSpacing.lg, 100, ), children: [ for (final genre in filtered.map((b) => b.genre).toSet()) Container( margin: const EdgeInsets.only( bottom: AppSpacing.sm, ), decoration: BoxDecoration( color: colorScheme.surface, border: Border.all( color: colorScheme.outline, ), borderRadius: BorderRadius.circular( AppSpacing.radiusMedium, ), ), child: ListTile( title: Text( genre, style: textTheme.titleMedium, ), trailing: Text( '${filtered.where((b) => b.genre == genre).length}', style: textTheme.bodyMedium?.copyWith( color: colorScheme.onSurface .withValues(alpha: 0.6), ), ), ), ), ], ), ), ], ), ), Positioned( bottom: 16, right: 20, child: FloatingActionButton( onPressed: () { Navigator.of(context, rootNavigator: true).push( MaterialPageRoute( builder: (_) => const AddBookScreen(), ), ); }, child: const Icon(Icons.add), ), ), ], ); }, ); }, ); } Widget _tab(BuildContext context, String label, int index, int currentIndex) { final selected = currentIndex == index; final colorScheme = Theme.of(context).colorScheme; final textTheme = Theme.of(context).textTheme; final disableAnimations = MediaQuery.of(context).disableAnimations; return GestureDetector( onTap: () => context.read().add(ChangeTab(index)), child: AnimatedContainer( duration: disableAnimations ? Duration.zero : const Duration(milliseconds: 200), padding: const EdgeInsets.symmetric( horizontal: AppSpacing.md, vertical: AppSpacing.sm, ), decoration: BoxDecoration( color: selected ? colorScheme.primary : colorScheme.surfaceContainerHighest, border: Border.all( color: selected ? colorScheme.primary : colorScheme.outline, ), borderRadius: BorderRadius.circular(AppSpacing.radiusPill), ), child: Text( label, style: textTheme.labelMedium?.copyWith( color: selected ? Colors.white : colorScheme.onSurface.withValues(alpha: 0.7), ), ), ), ); } }