hexsis_infinity_scroll_bloc 0.2.2
hexsis_infinity_scroll_bloc: ^0.2.2 copied to clipboard
BLoC/Cubit extension for hexsis_infinity_scroll with zero boilerplate and full scroll integration.
example/lib/main.dart
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hexsis_infinity_scroll_bloc/hexsis_infinity_scroll_bloc.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hexsis Infinity Scroll - BLoC Example',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: const HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('BLoC/Cubit Integration'),
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
_ExampleCard(
title: 'BLoC ListView',
description: 'Zero boilerplate with InfiniteScrollCubit',
onTap:
() => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const BlocListExample(),
),
),
),
_ExampleCard(
title: 'BLoC GridView',
description: 'Grid layout with cubit integration',
onTap:
() => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const BlocGridExample(),
),
),
),
],
),
);
}
}
class _ExampleCard extends StatelessWidget {
const _ExampleCard({
required this.title,
required this.description,
required this.onTap,
});
final String title;
final String description;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.only(bottom: 12),
child: ListTile(
title: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
subtitle: Text(description),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
onTap: onTap,
),
);
}
}
class ProductCubit extends InfiniteScrollCubit<Product> {
@override
Future<List<Product>> fetchPage(int page) async {
return await ProductApi.fetchProducts(page);
}
}
class BlocListExample extends StatelessWidget {
const BlocListExample({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => ProductCubit(),
child: Scaffold(
appBar: AppBar(
title: const Text('BLoC ListView'),
actions: [
BlocBuilder<ProductCubit, InfiniteScrollState<Product>>(
builder: (context, state) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Center(
child: Text(
'${state.items.length} items',
style: const TextStyle(fontWeight: FontWeight.bold),
),
),
);
},
),
],
),
body: InfiniteScrollBlocListView<Product, ProductCubit>(
itemBuilder: (context, product, index) {
return ProductListTile(product: product, index: index);
},
separatorBuilder: (context, index) => const Divider(height: 1),
),
),
);
}
}
class BlocGridExample extends StatelessWidget {
const BlocGridExample({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => ProductCubit(),
child: Scaffold(
appBar: AppBar(title: const Text('BLoC GridView')),
body: InfiniteScrollBlocGridView<Product, ProductCubit>(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
childAspectRatio: 0.75,
),
itemBuilder: (context, product, index) {
return ProductGridCard(product: product);
},
),
),
);
}
}
class Product {
final int id;
final String name;
final String description;
final double price;
Product({
required this.id,
required this.name,
required this.description,
required this.price,
});
}
class ProductApi {
static Future<List<Product>> fetchProducts(int page) async {
await Future.delayed(const Duration(milliseconds: 800));
if (page > 10) {
return [];
}
return List.generate(20, (index) {
final id = (page - 1) * 20 + index + 1;
return Product(
id: id,
name: 'Product #$id',
description: 'Description for product $id',
price: Random().nextDouble() * 100 + 10,
);
});
}
}
class ProductListTile extends StatelessWidget {
const ProductListTile({
required this.product,
required this.index,
super.key,
});
final Product product;
final int index;
@override
Widget build(BuildContext context) {
return ListTile(
leading: CircleAvatar(child: Text('#${index + 1}')),
title: Text(product.name),
subtitle: Text(product.description),
trailing: Text(
'\$${product.price.toStringAsFixed(2)}',
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
);
}
}
class ProductGridCard extends StatelessWidget {
const ProductGridCard({required this.product, super.key});
final Product product;
@override
Widget build(BuildContext context) {
return Card(
clipBehavior: Clip.antiAlias,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: Container(
color: Colors.primaries[product.id % Colors.primaries.length],
child: Center(
child: Text(
'#${product.id}',
style: const TextStyle(
color: Colors.white,
fontSize: 32,
fontWeight: FontWeight.bold,
),
),
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name,
style: const TextStyle(fontWeight: FontWeight.bold),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 4),
Text(
'\$${product.price.toStringAsFixed(2)}',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
);
}
}