fst 0.4.0
fst: ^0.4.0 copied to clipboard
State tree interface implementation for Flutter + Flutter specific APIs
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:fst/fst.dart';
void main() {
runApp(const CounterApp());
}
const $count = Context<int>(#count);
class IntCounter extends Impl<int> {
final int initialValue;
const IntCounter(super.n, [this.initialValue = 0]);
@override
int init(StateTreeNode n) => initialValue;
void increment([covariant int delta = 1]) => value += delta;
void decrement([covariant int delta = 1]) => value -= delta;
void reset() => value = 0;
}
const $modifier = Context<bool>(#modifier);
const $modifierImpl = Context<ModifierImpl>(#modifier_impl);
class ModifierImpl extends Impl<bool> {
const ModifierImpl(super.n);
@override
bool init(StateTreeNode n) => false;
void onKey(RawKeyEvent e) {
value = e.isShiftPressed;
}
@override
void build(StateTreeNode n) {
$modifier.of(n).provide(value);
$modifierImpl.of(n).provide(this);
}
}
class AppCounter extends IntCounter implements IncrementFABApi, ResetButtonApi {
const AppCounter(super.n);
@override
void build(StateTreeNode n) {
$count.of(n).provide(value);
Home.$incrementEnabled.of(n).provide(value < 25);
IncrementFAB.$api.of(n).provide(this);
ResetButton.$api.of(n).provide(this);
}
}
class CounterApp extends StatelessWidget {
const CounterApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: mountMany([
impl(ModifierImpl.new),
impl(AppCounter.new),
$count.listener((context, value) {
if (value % 10 != 0 || value == 0) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Count is multiple of 10')),
);
}, mode: InvocationMode.postFrame),
builder((context, n, child) {
final focusNode = FocusNode();
final impl = $modifierImpl.of(n).read();
return RawKeyboardListener(
focusNode: focusNode,
autofocus: true,
onKey: impl.onKey,
child: child,
);
}),
child(const Home()),
]),
);
}
}
class Home extends StateTreeWidget<void> {
static const $incrementEnabled = Context<bool>(#increment_fab_enabled);
const Home({super.key});
@override
Widget build(BuildContext context, StateTreeNode n) {
final fab = $incrementEnabled.of(n).query((e) {
if (!e) return null;
return const IncrementFAB();
});
return Scaffold(
appBar: AppBar(
title: const Text('State Tree Example'),
actions: const [
ResetButton(),
],
),
body: Center(
child: StateTreeBuilder((context, n, _) {
final count = n.watch($count);
return Text('Count: $count');
}),
),
floatingActionButton: fab,
);
}
}
abstract interface class IncrementFABApi {
void increment([int delta = 1]);
}
class IncrementFAB extends StateTreeWidget {
static const $api = Context<IncrementFABApi>(#increment_fab_api);
const IncrementFAB({super.key});
@override
Widget build(BuildContext context, StateTreeNode n) {
final api = n.read($api);
return FloatingActionButton(
onPressed: () {
final incrementBy10 = $modifier.of(n).read();
api.increment(incrementBy10 ? 10 : 1);
},
child: const Icon(Icons.add),
);
}
}
abstract interface class ResetButtonApi {
void reset();
}
class ResetButton extends StateTreeWidget {
static const $api = Context<ResetButtonApi>(#reset_button_api);
const ResetButton({super.key});
@override
Widget build(BuildContext context, StateTreeNode n) {
final api = $api.of(n).read();
return IconButton(
onPressed: api.reset,
icon: const Icon(Icons.refresh),
);
}
}