Skip to content

Instantly share code, notes, and snippets.

@brianegan
Created February 21, 2019 11:57
Show Gist options
  • Save brianegan/484c871754beac867d565d593f1152e3 to your computer and use it in GitHub Desktop.
Save brianegan/484c871754beac867d565d593f1152e3 to your computer and use it in GitHub Desktop.
Shows how to use a FakeStopwatch in a Widget test
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:quiver/async.dart';
import 'package:quiver/testing/time.dart';
void main() {
group('CounterTimer', () {
testWidgets('counts down from 10 to 0', (tester) async {
// Capture the current time
int now = DateTime.now().microsecondsSinceEpoch;
// Create a Timer
final timer = CountdownTimer(
Duration(seconds: 10),
Duration(milliseconds: 100),
stopwatch: FakeStopwatch(
// The function will simply return our fake "Now" time.
() => now,
Duration(seconds: 1).inMicroseconds,
),
);
// Create the Widget we'll rebuild over time
final widget = Directionality(
textDirection: TextDirection.ltr,
child: CountdownTimerRunner(countdownTimer: timer),
);
// Create a function that advances the current
Future<void> advanceAndPump([Duration duration = Duration.zero]) async {
// We advance the "now" time by the given duration. This simulates
// moving the stopwatch forward in time by that Duration.
now = now + duration.inMicroseconds;
await tester.pumpWidget(widget, duration);
}
// Build the Widget the first time and make sure it shows the right thing
await advanceAndPump();
expect(find.text('10'), findsOneWidget);
// Wait 10 seconds to make sure the Widget is at 0
await advanceAndPump(Duration(seconds: 10));
expect(find.text('0'), findsOneWidget);
// Important: Cancel the Timer, or this test will throw an error that
// there are active async calls in progress!
timer.cancel();
});
});
}
class CountdownTimerWidget extends StatefulWidget {
@override
_CountdownTimerWidgetState createState() => _CountdownTimerWidgetState();
}
class _CountdownTimerWidgetState extends State<CountdownTimerWidget> {
CountdownTimer _countdownTimer;
@override
void dispose() {
_countdownTimer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (_countdownTimer == null) {
return CountdownTimerInitializer(
Duration.zero,
onDurationSelected: (Duration duration) {
setState(() {
_countdownTimer =
CountdownTimer(duration, Duration(milliseconds: 250));
});
},
);
}
return CountdownTimerRunner(
countdownTimer: _countdownTimer,
);
}
}
typedef void OnDurationSelected(Duration duration);
class CountdownTimerInitializer extends StatefulWidget {
final Duration initialDuration;
final OnDurationSelected onDurationSelected;
CountdownTimerInitializer(this.initialDuration,
{@required this.onDurationSelected, Key key})
: super(key: key);
@override
CountdownTimerInitializerState createState() {
return new CountdownTimerInitializerState(initialDuration);
}
}
class CountdownTimerInitializerState extends State<CountdownTimerInitializer> {
Duration _duration;
CountdownTimerInitializerState([Duration initialDuration = Duration.zero])
: _duration = initialDuration;
void _increment() {
setState(() {
_duration = _duration + Duration(seconds: 1);
});
}
void _decrement() {
setState(() {
var updatedDuration = _duration - Duration(seconds: 1);
if (updatedDuration <= Duration.zero) {
updatedDuration = Duration.zero;
}
_duration = updatedDuration;
});
}
bool get _isValid {
return _duration > Duration.zero;
}
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
_duration.inSeconds.toString(),
style: Theme.of(context).textTheme.display1,
key: ValueKey("configure-duration-text"),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
key: ValueKey("configure-decrement"),
onPressed: _isValid ? _decrement : null,
child: Text("-"),
),
RaisedButton(
key: ValueKey("configure-increment"),
onPressed: _increment,
child: Text("+"),
),
],
),
RaisedButton(
key: ValueKey("configure-start"),
onPressed:
_isValid ? () => widget.onDurationSelected(_duration) : null,
child: Text("Start"),
)
],
);
}
}
class CountdownTimerRunner extends StatelessWidget {
final CountdownTimer countdownTimer;
const CountdownTimerRunner({Key key, @required this.countdownTimer})
: super(key: key);
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
StreamBuilder<CountdownTimer>(
initialData: countdownTimer,
stream: countdownTimer,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(
countdownTimer.remaining.inSeconds.toString(),
key: ValueKey("runner-duration-text"),
);
} else {
return Container();
}
},
),
],
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment