An easy way to create Functional Components (FC) in Flutter, with composable hooks.
The FC has been deployed in some production app builds. FC aims to save your time.
- ⏱️ No need to generate codes
- 🖨️ No need to verbose StateXXXWidget & State<XXX> classes
- 📄 Tiny implementations without external deps
- 🪝 Built-in powerful composable hooks
- 🐇 Speed up developing
- 🎯 Focus on performance optimization
- 🧱 Hot reload
- ⚛️ React style friendly
dependencies:
flutter_fc: ^1.0.0
No need to create a StatefulWidget class and a State for it.
class Counter extends FCWidget {
const Counter({super.key});
@override
Widget build(BuildContext context) {
final (counter, setCounter) = useState(0);
return ElevatedButton(
onPressed: () => setCounter(counter + 1),
child: Text("Counter: $counter"),
);
}
}
Dynamically create a temporary widget type, Not recommended.
final Counter = defineFC((context, props) {
final (counter, setCounter) = useState(0);
return ElevatedButton(
onPressed: () => setCounter(counter + 1),
child: Text("Counter: $counter"),
);
});
Create or restore with initial value stored in element, and get a function to let it update and rebuild.
final (loading, setLoading) = useState(false);
useSetState
instead in case of just want to trigger an rebuild.
final update = useSetState();
update(); // trigger an rebuild
Return a function, call to get whether element is mounted.
final isMounted = useIsMounted();
Timer(const Duration(seconds: 3), () {
if (isMounted()) {
// element is still present
}
});
Retrieve current building element. It inherits BuildContext
so...
final context = useElement();
final theme = Theme.of(context);
final navigator = Navigator.of(context);
Post a callback, called on element's dependencies were changed.
Post a callback, called on element receives reassemble directive.
useReassemble(() => textController.clear());
Post a callback, called before element unmounts.
final timer = useMemo(() => Timer(...));
useDispose(timer.cancel);
Post a callback, called on dependencies are different from before.
final (flag, setFlag) = useState(false);
useDiff(() {
print("Flag is changed to: $flag");
// DO NOT TRIGGER UPDATE HERE setFlag(false);
}, [flag]);
Give a factory to create value, get the same object on each build until dependencies were changed.
final (percent, setPercent) = useState(20);
final prettierPercent = useMemo(() => "${percent} %", [percent]);
Create or restore with initial value stored in a Ref
, which holds the value only.
final timerRef = useRef<Timer>(); // nullable
final flagRef = useRefMust(false); // not null
Create or restore with initial value stored in an ValueNotifier
, which update listeners on its value has changed.
final loading = useValue(() => false);
setLoading(bool newValue) => loading.value = newValue;
return ValueListenableBuilder(
valueListenable: loading,
builder: (context, flag, child) => flag
? const Text("Loading")
: const SizedBox(),
);
Create or restore a disposable instance. It may be called with .disposed()
if it inherits from ChangeNotifier
or StreamSink
,
Commonly used descendant classes:
- ChangeNotifier
- ValueNotifier
- StreamController
- FocusNode
- TextEditingController
// auto disposed on unmount
final controller = useDisposable(() => TextEditingController());
React
Dart 3
MIT (c) 2023-present, Luo3House.