2020-04-22 20:10:57 +02:00
//Forked from https://github.com/cdharris/flutter_duration_picker
//Copyright https://github.com/cdharris
//License MIT https://github.com/cdharris/flutter_duration_picker/blob/master/LICENSE
import ' dart:async ' ;
import ' dart:math ' as math ;
import ' package:flutter/material.dart ' ;
import ' package:flutter/rendering.dart ' ;
import ' package:flutter/services.dart ' ;
import ' package:flutter/widgets.dart ' ;
2020-07-26 12:20:42 +02:00
const Duration _kDialAnimateDuration = Duration ( milliseconds: 200 ) ;
2020-04-22 20:10:57 +02:00
const double _kDurationPickerWidthPortrait = 328.0 ;
const double _kDurationPickerWidthLandscape = 512.0 ;
const double _kDurationPickerHeightPortrait = 380.0 ;
const double _kDurationPickerHeightLandscape = 304.0 ;
const double _kTwoPi = 2 * math . pi ;
const double _kPiByTwo = math . pi / 2 ;
const double _kCircleTop = _kPiByTwo ;
class _DialPainter extends CustomPainter {
const _DialPainter ( {
@ required this . context ,
@ required this . labels ,
@ required this . backgroundColor ,
@ required this . accentColor ,
@ required this . theta ,
@ required this . textDirection ,
@ required this . selectedValue ,
@ required this . pct ,
@ required this . multiplier ,
@ required this . secondHand ,
} ) ;
final List < TextPainter > labels ;
final Color backgroundColor ;
final Color accentColor ;
final double theta ;
final TextDirection textDirection ;
final int selectedValue ;
final BuildContext context ;
final double pct ;
final int multiplier ;
final int secondHand ;
@ override
void paint ( Canvas canvas , Size size ) {
2020-07-26 12:20:42 +02:00
const _epsilon = . 001 ;
const _sweep = _kTwoPi - _epsilon ;
const _startAngle = - math . pi / 2.0 ;
2020-04-22 20:10:57 +02:00
2020-07-26 12:20:42 +02:00
final radius = size . shortestSide / 2.0 ;
final center = Offset ( size . width / 2.0 , size . height / 2.0 ) ;
final centerPoint = center ;
2020-04-22 20:10:57 +02:00
2020-07-26 12:20:42 +02:00
var pctTheta = ( 0.25 - ( theta % _kTwoPi ) / _kTwoPi ) % 1.0 ;
2020-04-22 20:10:57 +02:00
// Draw the background outer ring
2020-07-26 12:20:42 +02:00
canvas . drawCircle ( centerPoint , radius , Paint ( ) . . color = backgroundColor ) ;
2020-04-22 20:10:57 +02:00
// Draw a translucent circle for every hour
2020-07-26 12:20:42 +02:00
for ( var i = 0 ; i < multiplier ; i = i + 1 ) {
2020-04-22 20:10:57 +02:00
canvas . drawCircle ( centerPoint , radius ,
2020-07-26 12:20:42 +02:00
Paint ( ) . . color = accentColor . withOpacity ( ( i = = 0 ) ? 0.3 : 0.1 ) ) ;
2020-04-22 20:10:57 +02:00
}
// Draw the inner background circle
canvas . drawCircle ( centerPoint , radius * 0.88 ,
2020-07-26 12:20:42 +02:00
Paint ( ) . . color = Theme . of ( context ) . canvasColor ) ;
2020-04-22 20:10:57 +02:00
// Get the offset point for an angle value of theta, and a distance of _radius
Offset getOffsetForTheta ( double theta , double _radius ) {
return center +
2020-07-26 12:20:42 +02:00
Offset ( _radius * math . cos ( theta ) , - _radius * math . sin ( theta ) ) ;
2020-04-22 20:10:57 +02:00
}
// Draw the handle that is used to drag and to indicate the position around the circle
2020-07-26 12:20:42 +02:00
final handlePaint = Paint ( ) . . color = accentColor ;
final handlePoint = getOffsetForTheta ( theta , radius - 10.0 ) ;
2020-04-22 20:10:57 +02:00
canvas . drawCircle ( handlePoint , 20.0 , handlePaint ) ;
// Draw the Text in the center of the circle which displays hours and mins
2020-07-26 12:20:42 +02:00
var minutes = ( multiplier = = 0 ) ? ' ' : " ${ multiplier } min " ;
2020-04-22 20:10:57 +02:00
// int minutes = (pctTheta * 60).round();
// minutes = minutes == 60 ? 0 : minutes;
2020-07-26 12:20:42 +02:00
var seconds = " $ secondHand " ;
2020-04-22 20:10:57 +02:00
2020-07-26 12:20:42 +02:00
var textDurationValuePainter = TextPainter (
2020-04-22 20:10:57 +02:00
textAlign: TextAlign . center ,
2020-07-26 12:20:42 +02:00
text: TextSpan (
2020-04-22 20:10:57 +02:00
text: ' $ minutes $ seconds ' ,
style: Theme . of ( context )
. textTheme
. headline4
. copyWith ( fontSize: size . shortestSide * 0.15 ) ) ,
textDirection: TextDirection . ltr )
. . layout ( ) ;
2020-07-26 12:20:42 +02:00
var middleForValueText = Offset (
2020-04-22 20:10:57 +02:00
centerPoint . dx - ( textDurationValuePainter . width / 2 ) ,
centerPoint . dy - textDurationValuePainter . height / 2 ) ;
textDurationValuePainter . paint ( canvas , middleForValueText ) ;
2020-07-26 12:20:42 +02:00
var textMinPainter = TextPainter (
2020-04-22 20:10:57 +02:00
textAlign: TextAlign . center ,
2020-07-26 12:20:42 +02:00
text: TextSpan (
2020-04-22 20:10:57 +02:00
text: ' sec ' , //th: ${theta}',
style: Theme . of ( context ) . textTheme . bodyText1 ) ,
textDirection: TextDirection . ltr )
. . layout ( ) ;
textMinPainter . paint (
canvas ,
2020-07-26 12:20:42 +02:00
Offset (
2020-04-22 20:10:57 +02:00
centerPoint . dx - ( textMinPainter . width / 2 ) ,
centerPoint . dy +
( textDurationValuePainter . height / 2 ) -
textMinPainter . height / 2 ) ) ;
// Draw an arc around the circle for the amount of the circle that has elapsed.
2020-07-26 12:20:42 +02:00
var elapsedPainter = Paint ( )
2020-04-22 20:10:57 +02:00
. . style = PaintingStyle . stroke
. . strokeCap = StrokeCap . round
. . color = accentColor . withOpacity ( 0.3 )
. . isAntiAlias = true
. . strokeWidth = radius * 0.12 ;
canvas . drawArc (
2020-07-26 12:20:42 +02:00
Rect . fromCircle (
2020-04-22 20:10:57 +02:00
center: centerPoint ,
radius: radius - radius * 0.12 / 2 ,
) ,
_startAngle ,
_sweep * pctTheta ,
false ,
elapsedPainter ,
) ;
// Paint the labels (the minute strings)
void paintLabels ( List < TextPainter > labels ) {
if ( labels = = null ) return ;
2020-07-26 12:20:42 +02:00
final labelThetaIncrement = - _kTwoPi / labels . length ;
var labelTheta = _kPiByTwo ;
2020-04-22 20:10:57 +02:00
2020-07-26 12:20:42 +02:00
for ( var label in labels ) {
final labelOffset = Offset ( - label . width / 2.0 , - label . height / 2.0 ) ;
2020-04-22 20:10:57 +02:00
label . paint (
canvas , getOffsetForTheta ( labelTheta , radius - 40.0 ) + labelOffset ) ;
labelTheta + = labelThetaIncrement ;
}
}
paintLabels ( labels ) ;
}
@ override
bool shouldRepaint ( _DialPainter oldPainter ) {
return oldPainter . labels ! = labels | |
oldPainter . backgroundColor ! = backgroundColor | |
oldPainter . accentColor ! = accentColor | |
oldPainter . theta ! = theta ;
}
}
class _Dial extends StatefulWidget {
const _Dial (
{ @ required this . duration ,
@ required this . onChanged ,
this . snapToMins = 1.0 } )
: assert ( duration ! = null ) ;
final Duration duration ;
final ValueChanged < Duration > onChanged ;
/// The resolution of mins of the dial, i.e. if snapToMins = 5.0, only durations of 5min intervals will be selectable.
final double snapToMins ;
@ override
2020-07-26 12:20:42 +02:00
_DialState createState ( ) = > _DialState ( ) ;
2020-04-22 20:10:57 +02:00
}
class _DialState extends State < _Dial > with SingleTickerProviderStateMixin {
@ override
void initState ( ) {
super . initState ( ) ;
2020-07-26 12:20:42 +02:00
_thetaController = AnimationController (
2020-04-22 20:10:57 +02:00
vsync: this ,
2020-10-03 10:57:50 +02:00
duration: _kDialAnimateDuration ,
2020-04-22 20:10:57 +02:00
) ;
2020-07-26 12:20:42 +02:00
_thetaTween = Tween < double > ( begin: _getThetaForDuration ( widget . duration ) ) ;
_theta = _thetaTween . animate (
CurvedAnimation ( parent: _thetaController , curve: Curves . fastOutSlowIn ) )
2020-04-22 20:10:57 +02:00
. . addListener ( ( ) = > setState ( ( ) { } ) ) ;
_thetaController . addStatusListener ( ( status ) {
// if (status == AnimationStatus.completed && _hours != _snappedHours) {
// _hours = _snappedHours;
if ( status = = AnimationStatus . completed ) {
_minutes = _minuteHand ( _turningAngle ) ;
_seconds = _secondHand ( _turningAngle ) ;
setState ( ( ) { } ) ;
}
} ) ;
// _hours = widget.duration.inHours;
_turningAngle = _kPiByTwo - widget . duration . inSeconds / 60.0 * _kTwoPi ;
_minutes = _minuteHand ( _turningAngle ) ;
_seconds = _secondHand ( _turningAngle ) ;
}
ThemeData themeData ;
MaterialLocalizations localizations ;
MediaQueryData media ;
@ override
void didChangeDependencies ( ) {
super . didChangeDependencies ( ) ;
assert ( debugCheckHasMediaQuery ( context ) ) ;
themeData = Theme . of ( context ) ;
localizations = MaterialLocalizations . of ( context ) ;
media = MediaQuery . of ( context ) ;
}
@ override
void dispose ( ) {
_thetaController . dispose ( ) ;
super . dispose ( ) ;
}
Tween < double > _thetaTween ;
Animation < double > _theta ;
AnimationController _thetaController ;
2020-07-26 12:20:42 +02:00
final double _pct = 0.0 ;
2020-04-22 20:10:57 +02:00
int _seconds = 0 ;
bool _dragging = false ;
int _minutes = 0 ;
double _turningAngle = 0.0 ;
static double _nearest ( double target , double a , double b ) {
return ( ( target - a ) . abs ( ) < ( target - b ) . abs ( ) ) ? a : b ;
}
void _animateTo ( double targetTheta ) {
2020-07-26 12:20:42 +02:00
final currentTheta = _theta . value ;
var beginTheta =
2020-04-22 20:10:57 +02:00
_nearest ( targetTheta , currentTheta , currentTheta + _kTwoPi ) ;
beginTheta = _nearest ( targetTheta , beginTheta , currentTheta - _kTwoPi ) ;
_thetaTween
. . begin = beginTheta
. . end = targetTheta ;
_thetaController
. . value = 0.0
. . forward ( ) ;
}
double _getThetaForDuration ( Duration duration ) {
return ( _kPiByTwo - ( duration . inSeconds % 60 ) / 60.0 * _kTwoPi ) % _kTwoPi ;
}
Duration _getTimeForTheta ( double theta ) {
return _angleToDuration ( _turningAngle ) ;
}
Duration _notifyOnChangedIfNeeded ( ) {
// final Duration current = _getTimeForTheta(_theta.value);
// var d = Duration(hours: _hours, minutes: current.inMinutes % 60);
_minutes = _minuteHand ( _turningAngle ) ;
_seconds = _secondHand ( _turningAngle ) ;
var d = _angleToDuration ( _turningAngle ) ;
widget . onChanged ( d ) ;
return d ;
}
void _updateThetaForPan ( ) {
setState ( ( ) {
2020-07-26 12:20:42 +02:00
final offset = _position - _center ;
final angle = ( math . atan2 ( offset . dx , offset . dy ) - _kPiByTwo ) % _kTwoPi ;
2020-04-22 20:10:57 +02:00
// Stop accidental abrupt pans from making the dial seem like it starts from 1h.
// (happens when wanting to pan from 0 clockwise, but when doing so quickly, one actually pans from before 0 (e.g. setting the duration to 59mins, and then crossing 0, which would then mean 1h 1min).
if ( angle > = _kCircleTop & &
_theta . value < = _kCircleTop & &
_theta . value > = 0.1 & & // to allow the radians sign change at 15mins.
_minutes = = 0 ) return ;
_thetaTween
. . begin = angle
. . end = angle ;
} ) ;
}
Offset _position ;
Offset _center ;
void _handlePanStart ( DragStartDetails details ) {
assert ( ! _dragging ) ;
_dragging = true ;
final RenderBox box = context . findRenderObject ( ) ;
_position = box . globalToLocal ( details . globalPosition ) ;
_center = box . size . center ( Offset . zero ) ;
//_updateThetaForPan();
_notifyOnChangedIfNeeded ( ) ;
}
void _handlePanUpdate ( DragUpdateDetails details ) {
2020-07-26 12:20:42 +02:00
var oldTheta = _theta . value ;
2020-04-22 20:10:57 +02:00
_position + = details . delta ;
_updateThetaForPan ( ) ;
2020-07-26 12:20:42 +02:00
var newTheta = _theta . value ;
2020-04-22 20:10:57 +02:00
// _updateRotations(oldTheta, newTheta);
_updateTurningAngle ( oldTheta , newTheta ) ;
_notifyOnChangedIfNeeded ( ) ;
}
int _minuteHand ( double angle ) {
return _angleToDuration ( angle ) . inMinutes . toInt ( ) ;
}
int _secondHand ( double angle ) {
// Result is in [0; 59], even if overall time is >= 1 hour
return ( _angleToSeconds ( angle ) % 60.0 ) . toInt ( ) ;
}
Duration _angleToDuration ( double angle ) {
return _secondToDuration ( _angleToSeconds ( angle ) ) ;
}
Duration _secondToDuration ( seconds ) {
return Duration (
minutes: ( seconds ~ / 60 ) . toInt ( ) , seconds: ( seconds % 60.0 ) . toInt ( ) ) ;
}
double _angleToSeconds ( double angle ) {
// Coordinate transformation from mathematical COS to dial COS
2020-07-26 12:20:42 +02:00
var dialAngle = _kPiByTwo - angle ;
2020-04-22 20:10:57 +02:00
// Turn dial angle into minutes, may go beyond 60 minutes (multiple turns)
return dialAngle / _kTwoPi * 60.0 ;
}
void _updateTurningAngle ( double oldTheta , double newTheta ) {
// Register any angle by which the user has turned the dial.
//
// The resulting turning angle fully captures the state of the dial,
// including multiple turns (= full hours). The [_turningAngle] is in
// mathematical coordinate system, i.e. 3-o-clock position being zero, and
// increasing counter clock wise.
// From positive to negative (in mathematical COS)
if ( newTheta > 1.5 * math . pi & & oldTheta < 0.5 * math . pi ) {
_turningAngle = _turningAngle - ( ( _kTwoPi - newTheta ) + oldTheta ) ;
}
// From negative to positive (in mathematical COS)
else if ( newTheta < 0.5 * math . pi & & oldTheta > 1.5 * math . pi ) {
_turningAngle = _turningAngle + ( ( _kTwoPi - oldTheta ) + newTheta ) ;
} else {
_turningAngle = _turningAngle + ( newTheta - oldTheta ) ;
}
}
void _handlePanEnd ( DragEndDetails details ) {
assert ( _dragging ) ;
_dragging = false ;
_position = null ;
_center = null ;
//_notifyOnChangedIfNeeded();
//_animateTo(_getThetaForDuration(widget.duration));
}
void _handleTapUp ( TapUpDetails details ) {
final RenderBox box = context . findRenderObject ( ) ;
_position = box . globalToLocal ( details . globalPosition ) ;
_center = box . size . center ( Offset . zero ) ;
_updateThetaForPan ( ) ;
_notifyOnChangedIfNeeded ( ) ;
_animateTo ( _getThetaForDuration ( _getTimeForTheta ( _theta . value ) ) ) ;
_dragging = false ;
_position = null ;
_center = null ;
}
List < TextPainter > _buildSeconds ( TextTheme textTheme ) {
2020-07-26 12:20:42 +02:00
final style = textTheme . subtitle1 ;
const _secondsMarkerValues = < Duration > [
Duration ( seconds: 0 ) ,
Duration ( seconds: 5 ) ,
Duration ( seconds: 10 ) ,
Duration ( seconds: 15 ) ,
Duration ( seconds: 20 ) ,
Duration ( seconds: 25 ) ,
Duration ( seconds: 30 ) ,
Duration ( seconds: 35 ) ,
Duration ( seconds: 40 ) ,
Duration ( seconds: 45 ) ,
Duration ( seconds: 50 ) ,
Duration ( seconds: 55 ) ,
2020-04-22 20:10:57 +02:00
] ;
2020-07-26 12:20:42 +02:00
final labels = < TextPainter > [ ] ;
for ( var duration in _secondsMarkerValues ) {
var painter = TextPainter (
text: TextSpan ( style: style , text: duration . inSeconds . toString ( ) ) ,
2020-04-22 20:10:57 +02:00
textDirection: TextDirection . ltr ,
) . . layout ( ) ;
labels . add ( painter ) ;
}
return labels ;
}
@ override
Widget build ( BuildContext context ) {
Color backgroundColor ;
switch ( themeData . brightness ) {
case Brightness . light:
backgroundColor = Colors . grey [ 200 ] ;
break ;
case Brightness . dark:
backgroundColor = themeData . backgroundColor ;
break ;
}
2020-07-26 12:20:42 +02:00
final theme = Theme . of ( context ) ;
2020-04-22 20:10:57 +02:00
int selectedDialValue ;
_minutes = _minuteHand ( _turningAngle ) ;
_seconds = _secondHand ( _turningAngle ) ;
2020-07-26 12:20:42 +02:00
return GestureDetector (
2020-04-22 20:10:57 +02:00
excludeFromSemantics: true ,
onPanStart: _handlePanStart ,
onPanUpdate: _handlePanUpdate ,
onPanEnd: _handlePanEnd ,
onTapUp: _handleTapUp ,
2020-07-26 12:20:42 +02:00
child: CustomPaint (
painter: _DialPainter (
2020-04-22 20:10:57 +02:00
pct: _pct ,
multiplier: _minutes ,
secondHand: _seconds ,
context: context ,
selectedValue: selectedDialValue ,
labels: _buildSeconds ( theme . textTheme ) ,
backgroundColor: backgroundColor ,
accentColor: themeData . accentColor ,
theta: _theta . value ,
textDirection: Directionality . of ( context ) ,
) ,
) ) ;
}
}
/// A duration picker designed to appear inside a popup dialog.
///
/// Pass this widget to [showDialog]. The value returned by [showDialog] is the
/// selected [Duration] if the user taps the "OK" button, or null if the user
/// taps the "CANCEL" button. The selected time is reported by calling
/// [Navigator.pop].
class _DurationPickerDialog extends StatefulWidget {
/// Creates a duration picker.
///
/// [initialTime] must not be null.
const _DurationPickerDialog (
{ Key key , @ required this . initialTime , this . snapToMins } )
: assert ( initialTime ! = null ) ,
super ( key: key ) ;
/// The duration initially selected when the dialog is shown.
final Duration initialTime ;
final double snapToMins ;
@ override
2020-07-26 12:20:42 +02:00
_DurationPickerDialogState createState ( ) = > _DurationPickerDialogState ( ) ;
2020-04-22 20:10:57 +02:00
}
class _DurationPickerDialogState extends State < _DurationPickerDialog > {
@ override
void initState ( ) {
super . initState ( ) ;
_selectedDuration = widget . initialTime ;
}
@ override
void didChangeDependencies ( ) {
super . didChangeDependencies ( ) ;
localizations = MaterialLocalizations . of ( context ) ;
}
Duration get selectedDuration = > _selectedDuration ;
Duration _selectedDuration ;
MaterialLocalizations localizations ;
void _handleTimeChanged ( Duration value ) {
setState ( ( ) {
_selectedDuration = value ;
} ) ;
}
void _handleCancel ( ) {
Navigator . pop ( context ) ;
}
void _handleOk ( ) {
Navigator . pop ( context , _selectedDuration ) ;
}
@ override
Widget build ( BuildContext context ) {
assert ( debugCheckHasMediaQuery ( context ) ) ;
2020-07-26 12:20:42 +02:00
final theme = Theme . of ( context ) ;
2020-04-22 20:10:57 +02:00
2020-07-26 12:20:42 +02:00
final Widget picker = Padding (
2020-04-22 20:10:57 +02:00
padding: const EdgeInsets . all ( 16.0 ) ,
2020-07-26 12:20:42 +02:00
child: AspectRatio (
2020-04-22 20:10:57 +02:00
aspectRatio: 1.0 ,
2020-07-26 12:20:42 +02:00
child: _Dial (
2020-04-22 20:10:57 +02:00
duration: _selectedDuration ,
onChanged: _handleTimeChanged ,
snapToMins: widget . snapToMins ,
) ) ) ;
2020-04-25 15:50:27 +02:00
final Widget actions = ButtonBar ( children: < Widget > [
2020-07-26 12:20:42 +02:00
FlatButton (
child: Text ( localizations . cancelButtonLabel ) ,
2020-04-22 20:10:57 +02:00
onPressed: _handleCancel ) ,
2020-07-26 12:20:42 +02:00
FlatButton (
child: Text ( localizations . okButtonLabel ) , onPressed: _handleOk ) ,
2020-04-25 15:50:27 +02:00
] ) ;
2020-04-22 20:10:57 +02:00
2020-07-26 12:20:42 +02:00
final dialog =
Dialog ( child: OrientationBuilder ( builder: ( context , orientation ) {
final Widget pickerAndActions = Container (
2020-04-22 20:10:57 +02:00
color: theme . dialogBackgroundColor ,
2020-07-26 12:20:42 +02:00
child: Column (
2020-04-22 20:10:57 +02:00
mainAxisSize: MainAxisSize . min ,
children: < Widget > [
2020-07-26 12:20:42 +02:00
Expanded (
2020-04-22 20:10:57 +02:00
child:
picker ) , // picker grows and shrinks with the available space
actions ,
] ,
) ,
) ;
assert ( orientation ! = null ) ;
switch ( orientation ) {
case Orientation . portrait:
2020-07-26 12:20:42 +02:00
return SizedBox (
2020-04-22 20:10:57 +02:00
width: _kDurationPickerWidthPortrait ,
height: _kDurationPickerHeightPortrait ,
2020-07-26 12:20:42 +02:00
child: Column (
2020-04-22 20:10:57 +02:00
mainAxisSize: MainAxisSize . min ,
crossAxisAlignment: CrossAxisAlignment . stretch ,
children: < Widget > [
2020-07-26 12:20:42 +02:00
Expanded (
2020-04-22 20:10:57 +02:00
child: pickerAndActions ,
) ,
] ) ) ;
case Orientation . landscape:
2020-07-26 12:20:42 +02:00
return SizedBox (
2020-04-22 20:10:57 +02:00
width: _kDurationPickerWidthLandscape ,
height: _kDurationPickerHeightLandscape ,
2020-07-26 12:20:42 +02:00
child: Row (
2020-04-22 20:10:57 +02:00
mainAxisSize: MainAxisSize . min ,
crossAxisAlignment: CrossAxisAlignment . stretch ,
children: < Widget > [
2020-07-26 12:20:42 +02:00
Flexible (
2020-04-22 20:10:57 +02:00
child: pickerAndActions ,
) ,
] ) ) ;
}
return null ;
} ) ) ;
2020-07-26 12:20:42 +02:00
return Theme (
2020-04-22 20:10:57 +02:00
data: theme . copyWith (
dialogBackgroundColor: Colors . transparent ,
) ,
child: dialog ,
) ;
}
@ override
void dispose ( ) {
super . dispose ( ) ;
}
}
/// Shows a dialog containing the duration picker.
///
/// The returned Future resolves to the duration selected by the user when the user
/// closes the dialog. If the user cancels the dialog, null is returned.
///
/// To show a dialog with [initialTime] equal to the current time:
///
/// ```dart
/// showDurationPicker(
/// initialTime: new Duration.now(),
/// context: context,
/// );
/// ```
Future < Duration > showDurationPicker (
{ @ required BuildContext context ,
@ required Duration initialTime ,
double snapToMins } ) async {
assert ( context ! = null ) ;
assert ( initialTime ! = null ) ;
return await showDialog < Duration > (
context: context ,
2020-07-26 12:20:42 +02:00
builder: ( context ) = >
_DurationPickerDialog ( initialTime: initialTime , snapToMins: snapToMins ) ,
2020-04-22 20:10:57 +02:00
) ;
}
class DurationPicker extends StatelessWidget {
final Duration duration ;
final ValueChanged < Duration > onChange ;
final double snapToMins ;
final double width ;
final double height ;
DurationPicker (
{ this . duration = const Duration ( minutes: 0 ) ,
@ required this . onChange ,
this . snapToMins ,
this . width ,
2020-09-18 17:01:00 +02:00
this . height ,
Key key } )
: super ( key: key ) ;
2020-04-22 20:10:57 +02:00
@ override
Widget build ( BuildContext context ) {
return SizedBox (
2020-09-18 17:01:00 +02:00
width: width ? ? _kDurationPickerWidthPortrait / 1.5 ,
height: height ? ? _kDurationPickerHeightPortrait / 1.5 ,
child: _Dial (
duration: duration ,
onChanged: onChange ,
snapToMins: snapToMins ,
) ,
) ;
2020-04-22 20:10:57 +02:00
}
}