Unverified Commit 0b939116 authored by Viet Do's avatar Viet Do Committed by GitHub

Add Cupertino Countdown Timer Picker (#20966)

Add a countdown timer picker as part of the Cupertino date picker.
parent f8a2fc7c
......@@ -3,7 +3,6 @@
// found in the LICENSE file.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'cupertino_navigation_demo.dart' show coolColorNames;
......@@ -20,8 +19,7 @@ class CupertinoPickerDemo extends StatefulWidget {
class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
int _selectedColorIndex = 0;
int _selectedHour = 0;
int _selectedMinute = 0;
Duration timer = new Duration();
Widget _buildMenu(List<Widget> children) {
return new Container(
......@@ -74,63 +72,6 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
);
}
Widget _buildAlarmPicker() {
return new Row(
children: <Widget>[
new Expanded(
child: new CupertinoPicker(
scrollController: new FixedExtentScrollController(
initialItem: _selectedHour,
),
offAxisFraction: -0.5,
useMagnifier: true,
magnification: 1.1,
itemExtent: _kPickerItemHeight,
backgroundColor: CupertinoColors.white,
onSelectedItemChanged: (int index) {
setState(() {
_selectedHour = index;
});
},
children: new List<Widget>.generate(24, (int index) {
return new Container(
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 32.0),
child: new Text(index.toString()),
);
}),
looping: true,
),
),
new Expanded(
child: new CupertinoPicker(
scrollController: new FixedExtentScrollController(
initialItem: _selectedMinute,
),
offAxisFraction: 0.5,
useMagnifier: true,
magnification: 1.1,
itemExtent: _kPickerItemHeight,
backgroundColor: CupertinoColors.white,
onSelectedItemChanged: (int index) {
setState(() {
_selectedMinute = index;
});
},
children: new List<Widget>.generate(60, (int index) {
return new Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.only(left: 32.0),
child: new Text(index.toString()),
);
}),
looping: true,
),
),
],
);
}
Widget _buildBottomPicker(Widget picker) {
return new Container(
height: _kPickerSheetHeight,
......@@ -151,9 +92,41 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
);
}
Widget _buildCountdownTimerPicker(BuildContext context) {
return new GestureDetector(
onTap: () {
showCupertinoModalPopup<void>(
context: context,
builder: (BuildContext context) {
return _buildBottomPicker(
new CupertinoTimerPicker(
initialTimerDuration: timer,
onTimerDurationChanged: (Duration newTimer) {
setState(() {
timer = newTimer;
});
},
),
);
},
);
},
child: _buildMenu(
<Widget>[
const Text('Countdown Timer'),
new Text(
'${timer.inHours}:'
'${(timer.inMinutes % 60).toString().padLeft(2,'0')}:'
'${(timer.inSeconds % 60).toString().padLeft(2,'0')}',
style: const TextStyle(color: CupertinoColors.inactiveGray),
),
]
),
);
}
@override
Widget build(BuildContext context) {
final String time = new DateFormat.Hm().format(new DateTime(2018, 1, 1, _selectedHour, _selectedMinute));
return new Scaffold(
appBar: new AppBar(
title: const Text('Cupertino Picker'),
......@@ -171,7 +144,7 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
const Padding(padding: EdgeInsets.only(top: 32.0)),
new GestureDetector(
onTap: () async {
await showModalBottomSheet<void>(
await showCupertinoModalPopup<void>(
context: context,
builder: (BuildContext context) {
return _buildBottomPicker(_buildColorPicker());
......@@ -179,42 +152,22 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
);
},
child: _buildMenu(
<Widget>[
const Text('Favorite Color'),
new Text(
coolColorNames[_selectedColorIndex],
style: const TextStyle(
color: CupertinoColors.inactiveGray
<Widget>[
const Text('Favorite Color'),
new Text(
coolColorNames[_selectedColorIndex],
style: const TextStyle(
color: CupertinoColors.inactiveGray
),
),
),
]
),
),
new GestureDetector(
onTap: () async {
await showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return _buildBottomPicker(_buildAlarmPicker());
},
);
},
child: _buildMenu(
<Widget>[
const Text('Alarm'),
new Text(
time,
style: const TextStyle(
color: CupertinoColors.inactiveGray
),
),
]
]
),
),
_buildCountdownTimerPicker(context),
],
),
),
),
);
}
}
}
\ No newline at end of file
......@@ -13,6 +13,7 @@ export 'src/cupertino/app.dart';
export 'src/cupertino/bottom_tab_bar.dart';
export 'src/cupertino/button.dart';
export 'src/cupertino/colors.dart';
export 'src/cupertino/date_picker.dart';
export 'src/cupertino/dialog.dart';
export 'src/cupertino/icons.dart';
export 'src/cupertino/localizations.dart';
......
This diff is collapsed.
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/cupertino.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('Countdown timer picker', () {
testWidgets('onTimerDurationChanged is not null', (WidgetTester tester) async {
expect(
() {
new CupertinoTimerPicker(onTimerDurationChanged: null);
},
throwsAssertionError,
);
});
testWidgets('initialTimerDuration falls within limit', (WidgetTester tester) async {
expect(
() {
new CupertinoTimerPicker(
onTimerDurationChanged: (_) {},
initialTimerDuration: const Duration(days: 1),
);
},
throwsAssertionError,
);
expect(
() {
new CupertinoTimerPicker(
onTimerDurationChanged: (_) {},
initialTimerDuration: const Duration(seconds: -1),
);
},
throwsAssertionError,
);
});
testWidgets('minuteInterval is positive and is a factor of 60', (WidgetTester tester) async {
expect(
() {
new CupertinoTimerPicker(
onTimerDurationChanged: (_) {},
minuteInterval: 0,
);
},
throwsAssertionError,
);
expect(
() {
new CupertinoTimerPicker(
onTimerDurationChanged: (_) {},
minuteInterval: -1,
);
},
throwsAssertionError,
);
expect(
() {
new CupertinoTimerPicker(
onTimerDurationChanged: (_) {},
minuteInterval: 7,
);
},
throwsAssertionError,
);
});
testWidgets('secondInterval is positive and is a factor of 60', (WidgetTester tester) async {
expect(
() {
new CupertinoTimerPicker(
onTimerDurationChanged: (_) {},
secondInterval: 0,
);
},
throwsAssertionError,
);
expect(
() {
new CupertinoTimerPicker(
onTimerDurationChanged: (_) {},
secondInterval: -1,
);
},
throwsAssertionError,
);
expect(
() {
new CupertinoTimerPicker(
onTimerDurationChanged: (_) {},
secondInterval: 7,
);
},
throwsAssertionError,
);
});
testWidgets('secondInterval is positive and is a factor of 60', (WidgetTester tester) async {
expect(
() {
new CupertinoTimerPicker(
onTimerDurationChanged: (_) {},
secondInterval: 0,
);
},
throwsAssertionError,
);
expect(
() {
new CupertinoTimerPicker(
onTimerDurationChanged: (_) {},
secondInterval: -1,
);
},
throwsAssertionError,
);
expect(
() {
new CupertinoTimerPicker(
onTimerDurationChanged: (_) {},
secondInterval: 7,
);
},
throwsAssertionError,
);
});
testWidgets('columns are ordered correctly when text direction is ltr', (WidgetTester tester) async {
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new CupertinoTimerPicker(
onTimerDurationChanged: (_) {},
initialTimerDuration: const Duration(hours: 12, minutes: 30, seconds: 59),
),
),
);
Offset lastOffset = tester.getTopLeft(find.text('12'));
expect(tester.getTopLeft(find.text('hours')).dx > lastOffset.dx, true);
lastOffset = tester.getTopLeft(find.text('hours'));
expect(tester.getTopLeft(find.text('30')).dx > lastOffset.dx, true);
lastOffset = tester.getTopLeft(find.text('30'));
expect(tester.getTopLeft(find.text('min')).dx > lastOffset.dx, true);
lastOffset = tester.getTopLeft(find.text('min'));
expect(tester.getTopLeft(find.text('59')).dx > lastOffset.dx, true);
lastOffset = tester.getTopLeft(find.text('59'));
expect(tester.getTopLeft(find.text('sec')).dx > lastOffset.dx, true);
});
testWidgets('columns are ordered correctly when text direction is rtl', (WidgetTester tester) async {
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.rtl,
child: new CupertinoTimerPicker(
onTimerDurationChanged: (_) {},
initialTimerDuration: const Duration(hours: 12, minutes: 30, seconds: 59),
),
),
);
Offset lastOffset = tester.getTopLeft(find.text('12'));
expect(tester.getTopLeft(find.text('hours')).dx > lastOffset.dx, false);
lastOffset = tester.getTopLeft(find.text('hours'));
expect(tester.getTopLeft(find.text('30')).dx > lastOffset.dx, false);
lastOffset = tester.getTopLeft(find.text('30'));
expect(tester.getTopLeft(find.text('min')).dx > lastOffset.dx, false);
lastOffset = tester.getTopLeft(find.text('min'));
expect(tester.getTopLeft(find.text('59')).dx > lastOffset.dx, false);
lastOffset = tester.getTopLeft(find.text('59'));
expect(tester.getTopLeft(find.text('sec')).dx > lastOffset.dx, false);
});
testWidgets('width of picker is consistent', (WidgetTester tester) async {
await tester.pumpWidget(
new SizedBox(
height: 400.0,
width: 400.0,
child: new Directionality(
textDirection: TextDirection.ltr,
child: new CupertinoTimerPicker(
onTimerDurationChanged: (_) {},
initialTimerDuration: const Duration(hours: 12, minutes: 30, seconds: 59),
),
),
),
);
// Distance between the first column and the last column.
final double distance =
tester.getCenter(find.text('sec')).dx - tester.getCenter(find.text('12')).dx;
await tester.pumpWidget(
new SizedBox(
height: 400.0,
width: 800.0,
child: new Directionality(
textDirection: TextDirection.ltr,
child: new CupertinoTimerPicker(
onTimerDurationChanged: (_) {},
initialTimerDuration: const Duration(hours: 12, minutes: 30, seconds: 59),
),
),
),
);
// Distance between the first and the last column should be the same.
expect(
tester.getCenter(find.text('sec')).dx - tester.getCenter(find.text('12')).dx,
distance,
);
});
});
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment