|
// *** |
|
// See comments at: https://gist.github.com/catalunha/580500220454241f2a762bdf5e2954b1 |
|
// *** |
|
|
|
// This code is distributed under the MIT License. |
|
// Copyright (c) 2018 Felix Angelov. |
|
// You can find the original at https://github.com/felangel/bloc. |
|
|
|
import 'package:flutter/material.dart'; |
|
import 'package:flutter_bloc/flutter_bloc.dart'; |
|
import 'package:provider/provider.dart'; |
|
|
|
void main() { |
|
Bloc.observer = AppBlocObserver(); |
|
runApp(const App()); |
|
} |
|
|
|
/// Custom [BlocObserver] that observes all bloc and cubit state changes. |
|
class AppBlocObserver extends BlocObserver { |
|
@override |
|
void onChange(BlocBase bloc, Change change) { |
|
super.onChange(bloc, change); |
|
if (bloc is Cubit) print(change); |
|
} |
|
|
|
@override |
|
void onTransition(Bloc bloc, Transition transition) { |
|
super.onTransition(bloc, transition); |
|
print(transition); |
|
} |
|
} |
|
|
|
/// {@template app} |
|
/// A [StatelessWidget] that: |
|
/// * uses [bloc](https://pub.dev/packages/bloc) and |
|
/// [flutter_bloc](https://pub.dev/packages/flutter_bloc) |
|
/// to manage the state of a counter and the app theme. |
|
/// {@endtemplate} |
|
class App extends StatelessWidget { |
|
/// {@macro app} |
|
const App({Key? key}) : super(key: key); |
|
|
|
@override |
|
Widget build(BuildContext context) { |
|
return BlocProvider( |
|
create: (_) => ThemeCubit(), |
|
child: const AppView(), |
|
); |
|
} |
|
} |
|
|
|
/// {@template app_view} |
|
/// A [StatelessWidget] that: |
|
/// * reacts to state changes in the [ThemeCubit] |
|
/// and updates the theme of the [MaterialApp]. |
|
/// * renders the [CounterPage]. |
|
/// {@endtemplate} |
|
class AppView extends StatelessWidget { |
|
/// {@macro app_view} |
|
const AppView({Key? key}) : super(key: key); |
|
|
|
@override |
|
Widget build(BuildContext context) { |
|
return BlocBuilder<ThemeCubit, ThemeData>( |
|
builder: (_, theme) { |
|
return MaterialApp( |
|
theme: theme, |
|
home: const CounterPage(), |
|
); |
|
}, |
|
); |
|
} |
|
} |
|
|
|
/// {@template counter_page} |
|
/// A [StatelessWidget] that: |
|
/// * provides a [CounterBloc] to the [CounterView]. |
|
/// {@endtemplate} |
|
/* |
|
// CounterPage0 |
|
class CounterPage extends StatelessWidget { |
|
/// {@macro counter_page} |
|
const CounterPage({Key? key}) : super(key: key); |
|
|
|
@override |
|
Widget build(BuildContext context) { |
|
return BlocProvider( |
|
create: (_) => CounterBloc(), |
|
child: CounterView(), |
|
); |
|
} |
|
} |
|
*/ |
|
/* |
|
// CounterPage1 |
|
class CounterPage extends StatelessWidget { |
|
/// {@macro counter_page} |
|
const CounterPage({Key? key}) : super(key: key); |
|
|
|
@override |
|
Widget build(BuildContext context) { |
|
return BlocProvider( |
|
create: (context) => CounterBloc(), |
|
child: CounterView(firstValue: context.read<CounterBloc>().state), |
|
); |
|
} |
|
} |
|
*/ |
|
/* |
|
// CounterPage2 |
|
class CounterPage extends StatelessWidget { |
|
/// {@macro counter_page} |
|
const CounterPage({Key? key}) : super(key: key); |
|
|
|
@override |
|
Widget build(BuildContext context) { |
|
return BlocProvider( |
|
create: (_) => CounterBloc(), |
|
child: Builder( |
|
builder: (context) => |
|
CounterView(firstValue: context.read<CounterBloc>().state)), |
|
); |
|
} |
|
} |
|
*/ |
|
|
|
// CounterPage3 |
|
class CounterPage extends StatelessWidget { |
|
/// {@macro counter_page} |
|
const CounterPage({Key? key}) : super(key: key); |
|
|
|
@override |
|
Widget build(BuildContext context) { |
|
return Provider( |
|
create: (_) => CounterBloc(), |
|
builder:(context, _) => CounterView(firstValue: context.read<CounterBloc>().state), |
|
); |
|
} |
|
} |
|
|
|
/// {@template counter_view} |
|
/// A [StatelessWidget] that: |
|
/// * demonstrates how to consume and interact with a [CounterBloc]. |
|
/// {@endtemplate} |
|
class CounterView extends StatelessWidget { |
|
final int firstValue; |
|
|
|
/// {@macro counter_view} |
|
CounterView({ |
|
Key? key, |
|
this.firstValue = -2, |
|
}) : super(key: key); |
|
late int secondValue; |
|
@override |
|
Widget build(BuildContext context) { |
|
secondValue = context.read<CounterBloc>().state; |
|
return Scaffold( |
|
appBar: AppBar(title: Text('Counter $firstValue $secondValue')), |
|
body: Center( |
|
child: BlocBuilder<CounterBloc, int>( |
|
builder: (context, count) { |
|
return Text('$count', |
|
style: Theme.of(context).textTheme.displayLarge); |
|
}, |
|
), |
|
), |
|
floatingActionButton: Column( |
|
crossAxisAlignment: CrossAxisAlignment.end, |
|
mainAxisAlignment: MainAxisAlignment.end, |
|
children: <Widget>[ |
|
FloatingActionButton( |
|
child: const Icon(Icons.add), |
|
onPressed: () => context.read<CounterBloc>().add(Increment()), |
|
), |
|
const SizedBox(height: 4), |
|
FloatingActionButton( |
|
child: const Icon(Icons.remove), |
|
onPressed: () => context.read<CounterBloc>().add(Decrement()), |
|
), |
|
const SizedBox(height: 4), |
|
FloatingActionButton( |
|
child: const Icon(Icons.brightness_6), |
|
onPressed: () => context.read<ThemeCubit>().toggleTheme(), |
|
), |
|
], |
|
), |
|
); |
|
} |
|
} |
|
|
|
/// Event being processed by [CounterBloc]. |
|
abstract class CounterEvent {} |
|
|
|
/// Notifies bloc to increment state. |
|
class Increment extends CounterEvent {} |
|
|
|
/// Notifies bloc to decrement state. |
|
class Decrement extends CounterEvent {} |
|
|
|
/// {@template counter_bloc} |
|
/// A simple [Bloc] that manages an `int` as its state. |
|
/// {@endtemplate} |
|
class CounterBloc extends Bloc<CounterEvent, int> { |
|
/// {@macro counter_bloc} |
|
CounterBloc() : super(0) { |
|
on<Increment>((event, emit) => emit(state + 1)); |
|
on<Decrement>((event, emit) => emit(state - 1)); |
|
} |
|
} |
|
|
|
/// {@template brightness_cubit} |
|
/// A simple [Cubit] that manages the [ThemeData] as its state. |
|
/// {@endtemplate} |
|
class ThemeCubit extends Cubit<ThemeData> { |
|
/// {@macro brightness_cubit} |
|
ThemeCubit() : super(_lightTheme); |
|
|
|
static final _lightTheme = ThemeData.light(useMaterial3: true); |
|
|
|
static final _darkTheme = ThemeData.dark(useMaterial3: true); |
|
|
|
/// Toggles the current brightness between light and dark. |
|
void toggleTheme() { |
|
emit(state.brightness == Brightness.dark ? _lightTheme : _darkTheme); |
|
} |
|
} |