-
-
Save rrousselGit/beaf7442a20ea7e2ed3f13bbd40984a8 to your computer and use it in GitHub Desktop.
A clock + its test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:math'; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter/rendering.dart'; | |
import 'package:flutter/scheduler.dart'; | |
import 'package:provider/provider.dart'; | |
import 'package:clock/clock.dart'; | |
void main() { | |
runApp( | |
Provider( | |
create: (_) => Clock(), | |
child: MyApp(), | |
), | |
); | |
} | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
showPerformanceOverlay: true, | |
home: MyHomePage(), | |
routes: { | |
'/whatever': (c) => Scaffold( | |
appBar: AppBar(title: Text('Whatever')), | |
body: Center( | |
child: RaisedButton( | |
onPressed: () => Navigator.pop(c), | |
child: Text('Return to previous page'), | |
), | |
), | |
) | |
}, | |
); | |
} | |
} | |
class MyHomePage extends StatelessWidget { | |
MyHomePage({Key key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: Text('Clock'), | |
), | |
body: Center( | |
child: AnimatedClock(), | |
), | |
floatingActionButton: FloatingActionButton( | |
onPressed: () { | |
Navigator.pushNamed(context, '/whatever'); | |
}, | |
child: Icon(Icons.edit), | |
), | |
); | |
} | |
} | |
class AnimatedClock extends StatefulWidget { | |
@override | |
AnimatedClockState createState() => AnimatedClockState(); | |
} | |
@visibleForTesting | |
class AnimatedClockState extends State<AnimatedClock> | |
with SingleTickerProviderStateMixin { | |
Ticker _ticker; | |
DateTime _initialTime; | |
DateTime _now; | |
@override | |
void initState() { | |
super.initState(); | |
_now = _initialTime = context.read<Clock>().now(); | |
_ticker = this.createTicker((elapsed) { | |
final newTime = _initialTime.add(elapsed); | |
// rebuild only if seconds changes instead of every frame | |
if (_now.second != newTime.second) { | |
setState(() => _now = newTime); | |
} | |
}); | |
_ticker.start(); | |
} | |
@override | |
void dispose() { | |
_ticker.dispose(); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
// A widget that renders an unanimated clock from a DateTime | |
return _ClockRenderer( | |
dateTime: _now, | |
); | |
} | |
} | |
class _ClockRenderer extends StatelessWidget { | |
const _ClockRenderer({ | |
Key key, | |
this.dateTime, | |
}) : super(key: key); | |
final DateTime dateTime; | |
@override | |
Widget build(BuildContext context) { | |
return Container( | |
width: 210, | |
height: 210, | |
decoration: BoxDecoration( | |
border: Border.all(width: 3, color: Colors.black), | |
borderRadius: BorderRadius.circular(210), | |
), | |
child: Stack( | |
children: <Widget>[ | |
Positioned( | |
top: 105, | |
left: 100, | |
child: Transform( | |
alignment: Alignment.topCenter, | |
transform: Matrix4.rotationZ(pi + pi / 24 * 2 * dateTime.hour), | |
child: Container(height: 90, width: 5, color: Colors.black), | |
), | |
), | |
Positioned( | |
top: 105, | |
left: 100, | |
child: Transform( | |
alignment: Alignment.topCenter, | |
transform: Matrix4.rotationZ(pi + pi / 60 * 2 * dateTime.minute), | |
child: Container(height: 50, width: 5, color: Colors.grey), | |
), | |
), | |
Positioned( | |
top: 105, | |
left: 101.5, | |
child: Transform( | |
alignment: Alignment.topCenter, | |
transform: Matrix4.rotationZ(pi + pi / 60 * 2 * dateTime.second), | |
child: Container(height: 90, width: 2, color: Colors.red), | |
), | |
), | |
], | |
), | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'package:flutter_test/flutter_test.dart'; | |
import 'package:my_clock/main.dart'; | |
import 'package:provider/provider.dart'; | |
void main() { | |
testWidgets('renders our clock', (tester) async { | |
await tester.pumpWidget( | |
Provider( | |
create: (_) => tester.binding.clock, | |
child: MaterialApp( | |
home: Center( | |
child: RepaintBoundary( | |
child: AnimatedClock(), | |
), | |
), | |
), | |
), | |
); | |
await expectLater( | |
find.byType(AnimatedClock), | |
matchesGoldenFile('initial_frame.png'), | |
); | |
await tester.pump(Duration(hours: 2, minutes: 42, seconds: 21)); | |
await expectLater( | |
find.byType(AnimatedClock), | |
matchesGoldenFile('updated_frame.png'), | |
); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The length of the hour hand:
is confusingly longer than the minute hand:
The following screenshot is a golden file rendered by
_ClockRenderer
, which was configured with:It took me a while to figure out that the hour hand is the black one. An hour hand and a minute hand with heights of 50 and 80 respectively would be better off: