base_flutter_bloc 0.0.5
base_flutter_bloc: ^0.0.5 copied to clipboard
Simplify BLoC usage: unified 4-state machine, auto loading/error UI, flushbar notifications, retry, pagination, debounce/throttle, safeEmit and more — all in one lightweight package.
import 'package:base_flutter_bloc/base_flutter_bloc.dart';
import 'package:base_flutter_bloc_example/blocs/posts_pagination_bloc.dart';
import 'package:base_flutter_bloc_example/repositories/post_repository.dart';
import 'package:base_flutter_bloc_example/screens/counter_screen.dart';
import 'package:base_flutter_bloc_example/screens/pagination_screen.dart';
import 'package:base_flutter_bloc_example/screens/post_screen.dart';
import 'package:base_flutter_bloc_example/screens/search_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
void main() {
// ── 1. Register BaseBlocObserver ──────────────────────────────────────────
//
// Logs every state transition to the console in debug mode.
// In production you would add a crash-reporter callback here, e.g.:
// onErrorCallback: (bloc, error, stack) =>
// FirebaseCrashlytics.instance.recordError(error, stack),
Bloc.observer = BaseBlocObserver(logTransitions: true, logEvents: true);
runApp(const ExampleApp());
}
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
// ── 2. Wrap the app with BaseFlutterBlocConfig ────────────────────────
//
// Here we use the defaults (no custom errorWidgetBuilder or
// showFlushBarCallback), but you could override them for the whole app:
//
// BaseFlutterBlocConfig(
// errorWidgetBuilder: (context, message, onRetry) =>
// MyErrorWidget(message: message, onRetry: onRetry),
// showFlushBarCallback: ({ required context, message, isError = false,
// title, messageColor, titleColor, durationSeconds = 3 }) {
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(content: Text(message ?? '')),
// );
// },
// child: ...,
// )
return BaseFlutterBlocConfig(
child: MaterialApp(
title: 'base_flutter_bloc example',
theme: ThemeData(colorSchemeSeed: Colors.indigo, useMaterial3: true),
home: const HomeScreen(),
),
);
}
}
// ── Home screen ─────────────────────────────────────────────────────────────
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('base_flutter_bloc'), centerTitle: true),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
const _SectionHeader('Core concepts'),
_DemoTile(
icon: Icons.bolt,
title: 'BaseBloc + executeWithErrorHandling',
subtitle: 'Retry logic • Error state • BaseBlocBuilder',
onTap: () => _push(context, const PostScreen()),
),
_DemoTile(
icon: Icons.exposure,
title: 'BaseCubit + safeEmit',
subtitle: 'Simple counter with async increment',
onTap: () => _push(context, const CounterScreen()),
),
const _SectionHeader('Transformers'),
_DemoTile(
icon: Icons.search,
title: 'debounce — Search',
subtitle: 'Request fires only after 400 ms of silence',
onTap: () => _push(context, const SearchScreen()),
),
const _SectionHeader('Pagination'),
_DemoTile(
icon: Icons.list,
title: 'BasePaginationBloc',
subtitle: 'Infinite scroll • Pull-to-refresh',
onTap: () => _push(
context,
BlocProvider(
create: (_) =>
PostsPaginationBloc(PostRepository())..loadFirstPage(),
child: const PaginationScreen(),
),
),
),
],
),
);
}
void _push(BuildContext context, Widget screen) {
Navigator.push(context, MaterialPageRoute(builder: (_) => screen));
}
}
class _SectionHeader extends StatelessWidget {
final String title;
const _SectionHeader(this.title);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 24, bottom: 8),
child: Text(
title.toUpperCase(),
style: Theme.of(context).textTheme.labelSmall?.copyWith(
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.w700,
letterSpacing: 1.2,
),
),
);
}
}
class _DemoTile extends StatelessWidget {
final IconData icon;
final String title;
final String subtitle;
final VoidCallback onTap;
const _DemoTile({
required this.icon,
required this.title,
required this.subtitle,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.only(bottom: 8),
child: ListTile(
leading: Icon(icon, color: Theme.of(context).colorScheme.primary),
title: Text(title, style: const TextStyle(fontWeight: FontWeight.w600)),
subtitle: Text(subtitle),
trailing: const Icon(Icons.chevron_right),
onTap: onTap,
),
);
}
}