clock
clock copied to clipboard
Flutter's WidgetsFlutterBinding.ensureInitialized() and withClock() - causes clock.now() to misbehave
If flutter app starts with:
void main() {
WidgetsFlutterBinding.ensureInitialized();
Then clock.now()
prints not mocked value if outside of build
method.
Minimum reproduceable code:
import 'package:clock/clock.dart';
import 'package:flutter/material.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
withClock(Clock.fixed(DateTime(1990)), () {
runApp(const MyApp());
});
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void _printClockNow() {
print('_printClockNow is: ${clock.now()}'); // prints real NOW
setState(() {});
}
@override
Widget build(BuildContext context) {
print('build clock.now() is: ${clock.now()}'); // prints mocked NOW
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[Dummy()],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _printClockNow,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
class Dummy extends StatelessWidget {
@override
Widget build(Object context) {
return Text('t: ${clock.now()}');
}
}
Expected behavior:
clock.now()
to have mocked value despite WidgetsFlutterBinding.ensureInitialized()
EDIT:
Changed because I discovered that culprit is WidgetsFlutterBinding.ensureInitialized()
.
@mpszczolinski - thanks for the report!
From the report, it sounds like you think the resolution here might lie in Flutter's WidgetsFlutterBinding.ensureInitialized() method. If so, you may want to re-file this issue in the https://github.com/flutter/flutter repo.
I encountered this behavior in a project of mine, the clock override is registered in the current Zone, and for some reason it seems that calling WidgetsFlutterBinding.ensureInitialized()
this way causes things like button callbacks to be executed in a different Zone, or at least one that doesn't see the fake clock (it can't be completely separate, static classes still have the same initialized members, I checked).
I don't have enough knowledge about how Zones work or how they're used inside the Flutter framework to comment on why this is happening. I will say that running WidgetsFlutterBinding.ensureInitialized()
as the first thing within the withClock()
call seems to resolve the issue for me, so putting that call as the first level within main()
may work around this problem.