I'm trying to use Provider
with a Stateless
widget page to update a countdown UI.
When I run all the below, I get both print messages in the console on each 1 second tick of the stopwatch:
e.g.
flutter: build()
flutter: _buildCountDown()
Also, the remaining
value doesn't actually update in the UI.
But if I:
StatefulWidget
Consumer<CountdownProvider>
syntax e.g.return Consumer<CountdownProvider>(
builder: (_, countdownProvider, __) {
return Text(countdownProvider.remaining.toString()),
});
...everything works as expected: the UI updates properly and I don't get the build()
printout, just the _buildCountDown()
, which is what I wanted.
Q) What am I doing wrong that means I can't make this a Stateless widget that updates as expected?
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => CountdownProvider(),
),
],
)
CountdownProvider
:class CountdownProvider extends ChangeNotifier {
int remaining = 0;
final Stopwatch _stopwatch = Stopwatch();
late Timer _timer;
void start(int val) {
remaining = val;
_timer = Timer.periodic(const Duration(seconds: 1), _onTick);
_stopwatch.start();
}
void _onTick(Timer timer) {
remaining--;
if (remaining < 0) {
return;
}
notifyListeners();
}
void stop(bool skipNotify) {
_timer.cancel();
_stopwatch.stop();
if (skipNotify) {
_stopwatch.reset();
remaining = 0;
return;
}
notifyListeners();
}
}
PageCountdown
:class PageCountdown extends StatelessWidget {
const PageCountdown({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
// 10 seconds default countdown
Provider.of<CountdownProvider>(context, listen: false).start(10);
print("build()");
return Scaffold(
body: _buildContent(context),
);
}
Widget _buildContent(BuildContext context) {
return SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildCountDown(context),
],
),
);
}
Widget _buildCountDown(BuildContext context) {
int remaining = Provider.of<CountdownProvider>(context).remaining;
print("_buildCountDown()");
return Text(
remaining.toString(),
);
}
}
PageCountdown
is the single widget, which is rebuilt when the counter ticks. start(10)
in the beginning of build
will reset remaining
to 10 every rebuild. Something like this will work:
class PageCountdown extends StatelessWidget {
const PageCountdown({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
// 10 seconds default countdown
Provider.of<CountdownProvider>(context, listen: false).start(10);
print("build()");
return const Scaffold(
body: Countdown(),
);
}
}
class Countdown extends StatelessWidget {
const Countdown({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildCountDown(context),
],
),
);
}
Widget _buildCountDown(BuildContext context) {
int remaining = Provider.of<CountdownProvider>(context).remaining;
print("_buildCountDown()");
return Text(
remaining.toString(),
);
}
}