import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:veloe_kemono_party_flutter/kemono_client.dart'; import 'package:veloe_kemono_party_flutter/models/creator.dart'; import 'package:veloe_kemono_party_flutter/models/post.dart'; import 'package:veloe_kemono_party_flutter/pages/posts_screen.dart'; import 'package:veloe_kemono_party_flutter/pages/widgets/creator_card.dart'; import 'package:veloe_kemono_party_flutter/pages/widgets/post_container.dart'; import 'package:veloe_kemono_party_flutter/posts_notifier.dart'; // Providers final searchQueryProvider = StateProvider((ref) => ''); final selectedCreatorProvider = StateProvider((ref) => null); final postsPageProvider = StateProvider((ref) => 0); final kemonoClientProvider = Provider((ref) => KemonoClient()); final creatorsProvider = FutureProvider>((ref) async { final query = ref.watch(searchQueryProvider); final client = ref.read(kemonoClientProvider); final creators = await client.getCreators(); return creators.where((creator) => creator.name.toLowerCase().contains(query.toLowerCase()) || creator.service.toLowerCase().contains(query.toLowerCase()) ).toList(); }); final postsProvider = StateNotifierProvider.autoDispose .family>, (String, String)>((ref, args) { final (creatorId, service) = args; return PostsNotifier( creatorId: creatorId, service: service, client: ref.read(kemonoClientProvider), ); }); // Image Caching final imageCacheManager = CacheManager( Config( 'kemono_images', stalePeriod: const Duration(days: 30), maxNrOfCacheObjects: 1024, fileService: HttpFileService(), ), ); // Navigation Items Model class NavItem { final String label; final IconData icon; final Widget page; const NavItem({ required this.label, required this.icon, required this.page, }); } // App Layout with Navigation class AppLayout extends ConsumerStatefulWidget { final List navItems; const AppLayout({super.key, required this.navItems}); @override ConsumerState createState() => _AppLayoutState(); } class _AppLayoutState extends ConsumerState { final _navIndex = StateProvider((ref) => 0); Widget _buildDesktopNavRail(int selectedIndex) { return NavigationRail( selectedIndex: selectedIndex, onDestinationSelected: (index) => ref.read(_navIndex.notifier).state = index, labelType: NavigationRailLabelType.all, destinations: widget.navItems.map((item) => NavigationRailDestination( icon: Icon(item.icon), label: Text(item.label), )).toList(), ); } Widget _buildMobileDrawer(int selectedIndex) { return Drawer( child: ListView( children: [ const DrawerHeader( decoration: BoxDecoration(color: Colors.blue), child: Text('Navigation', style: TextStyle(color: Colors.white)), ), ...widget.navItems.asMap().entries.map((entry) => ListTile( leading: Icon(entry.value.icon), title: Text(entry.value.label), selected: entry.key == selectedIndex, onTap: () { ref.read(_navIndex.notifier).state = entry.key; Navigator.pop(context); }, )), ], ), ); } @override Widget build(BuildContext context) { final selectedIndex = ref.watch(_navIndex); final isDesktop = MediaQuery.of(context).size.width >= 600; return Scaffold( appBar: AppBar( title: Text(widget.navItems[selectedIndex].label), leading: isDesktop ? null : Builder( builder: (context) => IconButton( icon: const Icon(Icons.menu), onPressed: () => Scaffold.of(context).openDrawer(), ), ), ), drawer: isDesktop ? null : _buildMobileDrawer(selectedIndex), body: Row( children: [ if (isDesktop) _buildDesktopNavRail(selectedIndex), Expanded(child: widget.navItems[selectedIndex].page), ], ), ); } } class HomeScreen extends ConsumerStatefulWidget { const HomeScreen({super.key}); @override ConsumerState createState() => _HomeScreenState(); } class _HomeScreenState extends ConsumerState { final TextEditingController _searchController = TextEditingController(); final _searchQuery = StateProvider((ref) => ''); // Added search state // Modified provider with search filtering final filteredCreatorsProvider = FutureProvider.autoDispose.family, String>( (ref, query) async { final originalList = await ref.watch(creatorsProvider.future); return originalList.where( (creator) => creator.name.toLowerCase().contains(query.toLowerCase()) ).toList(); }, ); @override Widget build(BuildContext context) { return DefaultTabController( length: 2, child: Scaffold( appBar: AppBar( title: _buildSearchField(), // Replaced title with search field actions: [ IconButton( icon: const Icon(Icons.refresh), onPressed: () => ref.invalidate(filteredCreatorsProvider), ), ], ), body: _buildCreatorsList(ref.watch( filteredCreatorsProvider(ref.watch(_searchQuery)) // Added query param )), ), ); } // New search field widget Widget _buildSearchField() { return TextField( controller: _searchController, decoration: InputDecoration( hintText: 'Search creators...', border: InputBorder.none, suffixIcon: IconButton( icon: const Icon(Icons.clear), onPressed: () { _searchController.clear(); ref.read(_searchQuery.notifier).state = ''; }, ), ), onChanged: (value) => ref.read(_searchQuery.notifier).state = value, ); } // Updated creators list with search Widget _buildCreatorsList(AsyncValue> creatorsAsync) { return RefreshIndicator( onRefresh: () => ref.refresh(filteredCreatorsProvider(ref.read(_searchQuery)).future), child: creatorsAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (error, _) => Center(child: Text(error.toString())), data: (creators) => creators.isEmpty ? const Center(child: Text('No matching creators found')) : ListView.separated( padding: const EdgeInsets.all(16), itemCount: creators.length, separatorBuilder: (_, __) => const SizedBox(height: 8), itemBuilder: (context, index) => CreatorCard(creator: creators[index]), ), ), ); } Widget _buildGlobalPostsGrid() { final postsAsync = ref.watch(postsProvider(('82522', 'patreon'))); return postsAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (error, _) => Center(child: Text(error.toString())), data: (posts) => CustomScrollView( slivers: [ SliverAppBar(), SliverList( delegate: SliverChildBuilderDelegate( (context, index) => PostContainer(post: posts[index]), childCount: posts.length, ), ), ], ), ); } } // Main Application void main() => runApp( const ProviderScope( child: MaterialApp( home: AppLayout( navItems: [ NavItem( label: 'Home', icon: Icons.home, page: HomeScreen(), ), NavItem( label: 'Global Feed (Rukis)', icon: Icons.public, page: PostsScreen( creatorId:'82522', service: 'patreon', withAppBar: false,), ), NavItem( label: 'Settings', icon: Icons.settings, page: Placeholder(), // Replace with actual SettingsScreen ), ], ), ), ), );