run_once 1.0.7+3 run_once: ^1.0.7+3 copied to clipboard
An extremely simple library for running 'things' exactly once. No matter how many times that 'thing' is called.
run_once #
An extremely simple library for running 'things' exactly once. No matter how many times that 'thing' is called.
How does it work? #
Well it's very simple. We basically use the StackTrace.current
and carefully use this to keep record on when a function has been called. It means that the call stack is basically used as a unique identifier for when and where a function has been called.
Rationale #
This library could be considered an anti-pattern in itself and a little exercise, but imagine a Flutter StatefulWidget
with its build
that you cannot rely on being called only when you yourself call setState
. In this case you could use runOnce
to run something once during this widget's lifetime no matter the times build
was called.
Worth a mention; there also is the AsyncMemoizer.runOnce
for async
functions from package:async
but that one isn't aware of the call stack and works internally in a much simpler manner.
Usage #
Pure dart example;
import 'package:run_once/run_once.dart';
void main() async {
/// This will print 'called 1' exactly 1 time although
/// we call this function 10 times.
for (var i = 0; i < 10; i++) {
runOnce(() {
print('called 1');
});
}
/// This will print 'called 2' exactly 2 times as the [runOnce] simply
/// isn't called in the same spot.
runOnce(() {
print('called 2');
});
runOnce(() {
print('called 2');
});
/// This will print 'called 3' exactly 3 times although
/// we call this function 15 times.
/// Note the `forDuration` is exactly a [Duration] of 500 milliseconds.
/// Using `forDuration` means for a duration of 500 milliseconds [runOnce]
/// can only run once.
/// So we loop 15 times in this loop and wait 100 milliseconds in each
/// loop cycle; thus 'called 3' is printed exactly 3 times.
for (var i = 0; i < 15; i++) {
await Future.delayed(Duration(milliseconds: 100));
runOnce(() {
print('called 3');
}, forDuration: Duration(milliseconds: 500));
}
}
Flutter with a StatefulWidget
example;
import 'package:run_once/run_once.dart';
// This example uses the state_notifier package
class _SomeWidgetState extends State<SomeWidget> {
@override
void dispose() {
/// When this is called [runOnce] will be able to run once again
/// for this class alone.
runOnceDestroy();
super.dispose();
}
@override
Widget build(BuildContext context) {
final state = context.watch<MyState>();
return state.when(
data: () => Placeholder(),
finished: () {
runOnce(() {
/// This is always only called once
/// until [dispose] is called.
Navigator.pushNamed(context, '/b');
});
return Placeholder();
},
);
}
}
Disclaimer #
This library isn't really tested very comprehensively at all. So it might not work in every use case.