nocterm_bloc 1.0.3
nocterm_bloc: ^1.0.3 copied to clipboard
Terminal-friendly Bloc & Cubit helpers for nocterm.dev and related CLI apps.
import 'package:nocterm/nocterm.dart';
import 'package:nocterm_bloc/nocterm_bloc.dart';
Future<void> main() => runApp(
NoctermApp(
home: BlocProvider(
create: (context) => CounterCubit(),
child: CounterDemo(),
),
),
);
class CounterDemo extends StatelessComponent {
static const maxCount = 20;
Component build(BuildContext context) {
return Focusable(
onKeyEvent: (event) {
final cubit = context.read<CounterCubit>();
if (event.logicalKey == LogicalKey.arrowRight) {
cubit.increment();
return true;
}
if (event.logicalKey == LogicalKey.arrowLeft) {
cubit.decrement();
return true;
}
if (event.logicalKey == LogicalKey.keyR) {
cubit.reset();
return true;
}
return false;
},
child: BlocListener<CounterCubit, int>(
listenWhen: (previous, current) => current != previous,
listener: (context, count) {
if (count == maxCount) {
Navigator.of(context)
.showDialog<bool>(builder: (context) => ConfirmDialogContent())
.then((value) => context.read<CounterCubit>().reset());
}
},
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const TitleLabel(),
const SizedBox(height: 1),
const CounterComponent(),
const SizedBox(height: 1),
const ProgressRow(),
const SizedBox(height: 1),
const HintsComponent(),
],
),
),
focused: true,
);
}
}
/// A simple component that displays the title of the app.
class TitleLabel extends StatelessComponent {
const TitleLabel();
Component build(BuildContext context) {
return Text(
'⚡ Counter',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
),
);
}
}
/// A component that listens to the `CounterCubit` and displays the current count.
class CounterComponent extends StatelessComponent {
const CounterComponent();
Component build(BuildContext context) {
return BlocBuilder<CounterCubit, int>(
builder: (context, count) {
return Text(
'$count',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.cyan,
),
);
},
);
}
}
/// A component that listens to the `CounterCubit` and displays a progress bar based on the current count.
class ProgressRow extends StatelessComponent {
const ProgressRow();
Component build(BuildContext context) {
return BlocBuilder<CounterCubit, int>(
builder: (context, count) {
final progress = (count % 20) / 20;
final barWidth = 30;
final filled = (progress * barWidth).round();
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('▕', style: TextStyle(color: Colors.gray)),
Text('█' * filled, style: TextStyle(color: Colors.magenta)),
Text(
'░' * (barWidth - filled),
style: TextStyle(color: Colors.gray),
),
Text('▏', style: TextStyle(color: Colors.gray)),
],
),
],
);
},
);
}
}
/// A component that displays hints for the user on how to interact with the app.
class HintsComponent extends StatelessComponent {
const HintsComponent();
Component build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Right arrow', style: TextStyle(color: Colors.yellow)),
Text(' +1 ', style: TextStyle(color: Colors.gray)),
Text('Left arrow', style: TextStyle(color: Colors.yellow)),
Text(' -1 ', style: TextStyle(color: Colors.gray)),
Text('R', style: TextStyle(color: Colors.yellow)),
Text(' reset', style: TextStyle(color: Colors.gray)),
],
);
}
}
class ConfirmDialogContent extends StatelessComponent {
Component build(BuildContext context) {
final theme = TuiTheme.of(context);
return Container(
width: 40,
height: 7,
padding: EdgeInsets.all(1),
decoration: BoxDecoration(
color: theme.success,
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'Max count reached!',
style: TextStyle(
color: theme.onSuccess,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 1),
Text(
'Press ESC to close.',
style: TextStyle(
color: theme.onSuccess,
fontWeight: FontWeight.bold,
),
),
],
),
);
}
}
/// A simple Cubit that manages an integer state representing a counter.
// ignore: prefer_file_naming_conventions
class CounterCubit extends Cubit<int> {
CounterCubit() : super(0);
void increment() => emit(state + 1);
void decrement() => emit(state - 1);
void reset() => emit(0);
}