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 @@ ...@@ -3,7 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'cupertino_navigation_demo.dart' show coolColorNames; import 'cupertino_navigation_demo.dart' show coolColorNames;
...@@ -20,8 +19,7 @@ class CupertinoPickerDemo extends StatefulWidget { ...@@ -20,8 +19,7 @@ class CupertinoPickerDemo extends StatefulWidget {
class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> { class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
int _selectedColorIndex = 0; int _selectedColorIndex = 0;
int _selectedHour = 0; Duration timer = new Duration();
int _selectedMinute = 0;
Widget _buildMenu(List<Widget> children) { Widget _buildMenu(List<Widget> children) {
return new Container( return new Container(
...@@ -74,63 +72,6 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> { ...@@ -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) { Widget _buildBottomPicker(Widget picker) {
return new Container( return new Container(
height: _kPickerSheetHeight, height: _kPickerSheetHeight,
...@@ -151,9 +92,41 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> { ...@@ -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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final String time = new DateFormat.Hm().format(new DateTime(2018, 1, 1, _selectedHour, _selectedMinute));
return new Scaffold( return new Scaffold(
appBar: new AppBar( appBar: new AppBar(
title: const Text('Cupertino Picker'), title: const Text('Cupertino Picker'),
...@@ -171,7 +144,7 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> { ...@@ -171,7 +144,7 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
const Padding(padding: EdgeInsets.only(top: 32.0)), const Padding(padding: EdgeInsets.only(top: 32.0)),
new GestureDetector( new GestureDetector(
onTap: () async { onTap: () async {
await showModalBottomSheet<void>( await showCupertinoModalPopup<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return _buildBottomPicker(_buildColorPicker()); return _buildBottomPicker(_buildColorPicker());
...@@ -179,42 +152,22 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> { ...@@ -179,42 +152,22 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
); );
}, },
child: _buildMenu( child: _buildMenu(
<Widget>[ <Widget>[
const Text('Favorite Color'), const Text('Favorite Color'),
new Text( new Text(
coolColorNames[_selectedColorIndex], coolColorNames[_selectedColorIndex],
style: const TextStyle( style: const TextStyle(
color: CupertinoColors.inactiveGray 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'; ...@@ -13,6 +13,7 @@ export 'src/cupertino/app.dart';
export 'src/cupertino/bottom_tab_bar.dart'; export 'src/cupertino/bottom_tab_bar.dart';
export 'src/cupertino/button.dart'; export 'src/cupertino/button.dart';
export 'src/cupertino/colors.dart'; export 'src/cupertino/colors.dart';
export 'src/cupertino/date_picker.dart';
export 'src/cupertino/dialog.dart'; export 'src/cupertino/dialog.dart';
export 'src/cupertino/icons.dart'; export 'src/cupertino/icons.dart';
export 'src/cupertino/localizations.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