Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
72d2706e
Commit
72d2706e
authored
Nov 25, 2015
by
Adam Barth
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Finish TimePicker
After this patch, TimePicker should work correctly. Fixes #559
parent
134b11e3
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
139 additions
and
32 deletions
+139
-32
time_picker_demo.dart
examples/material_gallery/lib/time_picker_demo.dart
+1
-1
constants.dart
packages/flutter/lib/src/material/constants.dart
+3
-2
time_picker.dart
packages/flutter/lib/src/material/time_picker.dart
+135
-29
No files found.
examples/material_gallery/lib/time_picker_demo.dart
View file @
72d2706e
...
...
@@ -29,7 +29,7 @@ class _TimePickerDemoState extends State<TimePickerDemo> {
Widget
build
(
BuildContext
context
)
{
return
new
Column
([
new
Text
(
'
$
{_selectedTime.hour}
:
${_selectedTime.minute}
'
),
new
Text
(
'
$
_selectedTime
'
),
new
RaisedButton
(
onPressed:
_handleSelectTime
,
child:
new
Text
(
'SELECT TIME'
)
...
...
packages/flutter/lib/src/material/constants.dart
View file @
72d2706e
...
...
@@ -2,8 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Modeled after Android's ViewConfiguration:
// https://github.com/android/platform_frameworks_base/blob/master/core/java/android/view/ViewConfiguration.java
import
'package:flutter/widgets.dart'
;
// TODO(ianh): Figure out actual specced height for status bar
const
double
kStatusBarHeight
=
50.0
;
...
...
@@ -32,3 +31,5 @@ const Duration kScrollbarFadeDelay = const Duration(milliseconds: 300);
const
double
kFadingEdgeLength
=
12.0
;
const
double
kPressedStateDuration
=
64.0
;
// units?
const
Duration
kThemeChangeDuration
=
const
Duration
(
milliseconds:
200
);
const
EdgeDims
kDialogHeadingPadding
=
const
EdgeDims
.
TRBL
(
24.0
,
24.0
,
20.0
,
24.0
);
packages/flutter/lib/src/material/time_picker.dart
View file @
72d2706e
...
...
@@ -4,6 +4,7 @@
import
'dart:math'
as
math
;
import
'package:flutter/animation.dart'
;
import
'package:flutter/painting.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/services.dart'
;
...
...
@@ -12,11 +13,27 @@ import 'package:flutter/widgets.dart';
import
'colors.dart'
;
import
'theme.dart'
;
import
'typography.dart'
;
import
'constants.dart'
;
const
Duration
_kDialAnimateDuration
=
const
Duration
(
milliseconds:
200
);
const
double
_kTwoPi
=
2
*
math
.
PI
;
const
int
_kHoursPerDay
=
24
;
const
int
_kHoursPerPeriod
=
12
;
const
int
_kMinutesPerHour
=
60
;
enum
DayPeriod
{
am
,
pm
,
}
/// A value representing a time during the day
class
TimeOfDay
{
const
TimeOfDay
({
this
.
hour
,
this
.
minute
});
/// Returns a new TimeOfDay with the hour and/or minute replaced.
TimeOfDay
replacing
({
int
hour
,
int
minute
})
{
assert
(
hour
==
null
||
(
hour
>=
0
&&
hour
<
_kHoursPerDay
));
assert
(
minute
==
null
||
(
minute
>=
0
&&
minute
<
_kMinutesPerHour
));
return
new
TimeOfDay
(
hour:
hour
??
this
.
hour
,
minute:
minute
??
this
.
minute
);
}
...
...
@@ -26,6 +43,39 @@ class TimeOfDay {
/// The selected minute.
final
int
minute
;
/// Whether this time of day is before or after noon.
DayPeriod
get
period
=>
hour
<
_kHoursPerPeriod
?
DayPeriod
.
am
:
DayPeriod
.
pm
;
/// Which hour of the current period (e.g., am or pm) this time is.
int
get
hourOfPeriod
=>
hour
-
periodOffset
;
String
_addLeadingZeroIfNeeded
(
int
value
)
{
if
(
value
<
10
)
return
'0
$value
'
;
return
value
.
toString
();
}
/// A string representing the hour, in 24 hour time (e.g., '04' or '18').
String
get
hourLabel
=>
_addLeadingZeroIfNeeded
(
hour
);
/// A string representing the minute (e.g., '07').
String
get
minuteLabel
=>
_addLeadingZeroIfNeeded
(
minute
);
/// A string representing the hour of the current period (e.g., '4' or '6').
String
get
hourOfPeriodLabel
{
// TODO(ianh): Localize.
final
int
hourOfPeriod
=
this
.
hourOfPeriod
;
if
(
hourOfPeriod
==
0
)
return
'12'
;
return
hourOfPeriod
.
toString
();
}
/// A string representing the current period (e.g., 'a.m.').
String
get
periodLabel
=>
period
==
DayPeriod
.
am
?
'a.m.'
:
'p.m.'
;
// TODO(ianh): Localize.
/// The hour at which the current period starts.
int
get
periodOffset
=>
period
==
DayPeriod
.
am
?
0
:
_kHoursPerPeriod
;
bool
operator
==(
dynamic
other
)
{
if
(
other
is
!
TimeOfDay
)
return
false
;
...
...
@@ -41,7 +91,8 @@ class TimeOfDay {
return
value
;
}
String
toString
()
=>
'TimeOfDay(hour:
$hour
, minute:
$minute
)'
;
// TODO(ianh): Localize.
String
toString
()
=>
'
$hourOfPeriodLabel
:
$minuteLabel
$periodLabel
'
;
}
enum
_TimePickerMode
{
hour
,
minute
}
...
...
@@ -74,7 +125,8 @@ class _TimePickerState extends State<TimePicker> {
Widget
header
=
new
_TimePickerHeader
(
selectedTime:
config
.
selectedTime
,
mode:
_mode
,
onModeChanged:
_handleModeChanged
onModeChanged:
_handleModeChanged
,
onChanged:
config
.
onChanged
);
return
new
Column
(<
Widget
>[
header
,
...
...
@@ -93,9 +145,14 @@ class _TimePickerState extends State<TimePicker> {
}
}
//
Shows the selected date in large font and toggles between year and day mode
//
TODO(ianh): Localize!
class
_TimePickerHeader
extends
StatelessComponent
{
_TimePickerHeader
({
this
.
selectedTime
,
this
.
mode
,
this
.
onModeChanged
})
{
_TimePickerHeader
({
this
.
selectedTime
,
this
.
mode
,
this
.
onModeChanged
,
this
.
onChanged
})
{
assert
(
selectedTime
!=
null
);
assert
(
mode
!=
null
);
}
...
...
@@ -103,12 +160,18 @@ class _TimePickerHeader extends StatelessComponent {
final
TimeOfDay
selectedTime
;
final
_TimePickerMode
mode
;
final
ValueChanged
<
_TimePickerMode
>
onModeChanged
;
final
ValueChanged
<
TimeOfDay
>
onChanged
;
void
_handleChangeMode
(
_TimePickerMode
value
)
{
if
(
value
!=
mode
)
onModeChanged
(
value
);
}
void
_handleChangeDayPeriod
()
{
int
newHour
=
(
selectedTime
.
hour
+
_kHoursPerPeriod
)
%
_kHoursPerDay
;
onChanged
(
selectedTime
.
replacing
(
hour:
newHour
));
}
Widget
build
(
BuildContext
context
)
{
ThemeData
theme
=
Theme
.
of
(
context
);
TextTheme
headerTheme
=
theme
.
primaryTextTheme
;
...
...
@@ -131,27 +194,45 @@ class _TimePickerHeader extends StatelessComponent {
TextStyle
hourStyle
=
mode
==
_TimePickerMode
.
hour
?
activeStyle
:
inactiveStyle
;
TextStyle
minuteStyle
=
mode
==
_TimePickerMode
.
minute
?
activeStyle
:
inactiveStyle
;
TextStyle
amStyle
=
headerTheme
.
subhead
.
copyWith
(
color:
selectedTime
.
period
==
DayPeriod
.
am
?
activeColor:
inactiveColor
);
TextStyle
pmStyle
=
headerTheme
.
subhead
.
copyWith
(
color:
selectedTime
.
period
==
DayPeriod
.
pm
?
activeColor:
inactiveColor
);
return
new
Container
(
padding:
new
EdgeDims
.
all
(
10.0
)
,
padding:
kDialogHeadingPadding
,
decoration:
new
BoxDecoration
(
backgroundColor:
theme
.
primaryColor
),
child:
new
Row
(<
Widget
>[
new
GestureDetector
(
onTap:
()
=>
_handleChangeMode
(
_TimePickerMode
.
hour
),
child:
new
Text
(
selectedTime
.
hour
.
toString
()
,
style:
hourStyle
)
child:
new
Text
(
selectedTime
.
hour
OfPeriodLabel
,
style:
hourStyle
)
),
new
Text
(
':'
,
style:
inactiveStyle
),
new
GestureDetector
(
onTap:
()
=>
_handleChangeMode
(
_TimePickerMode
.
minute
),
child:
new
Text
(
selectedTime
.
minute
.
toString
()
,
style:
minuteStyle
)
child:
new
Text
(
selectedTime
.
minute
Label
,
style:
minuteStyle
)
),
new
GestureDetector
(
onTap:
_handleChangeDayPeriod
,
behavior:
HitTestBehavior
.
opaque
,
child:
new
Container
(
padding:
const
EdgeDims
.
only
(
left:
16.0
,
right:
24.0
),
child:
new
Column
([
new
Text
(
'AM'
,
style:
amStyle
),
new
Container
(
padding:
const
EdgeDims
.
only
(
top:
4.0
),
child:
new
Text
(
'PM'
,
style:
pmStyle
)
),
],
justifyContent:
FlexJustifyContent
.
end
)
)
)
],
justifyContent:
FlexJustifyContent
.
end
)
);
}
}
final
List
<
TextPainter
>
_kHours
=
_initHours
();
final
List
<
TextPainter
>
_kMinutes
=
_initMinutes
();
List
<
TextPainter
>
_initPainters
(
List
<
String
>
labels
)
{
TextStyle
style
=
Typography
.
black
.
subhead
.
copyWith
(
height:
1.0
);
List
<
TextPainter
>
painters
=
new
List
<
TextPainter
>(
labels
.
length
);
...
...
@@ -215,7 +296,7 @@ class _DialPainter extends CustomPainter {
primaryPaint
.
strokeWidth
=
2.0
;
canvas
.
drawLine
(
centerPoint
,
currentPoint
,
primaryPaint
);
double
labelThetaIncrement
=
-
2
*
math
.
PI
/
_kHour
s
.
length
;
double
labelThetaIncrement
=
-
_kTwoPi
/
label
s
.
length
;
double
labelTheta
=
math
.
PI
/
2.0
;
for
(
TextPainter
label
in
labels
)
{
...
...
@@ -249,33 +330,54 @@ class _Dial extends StatefulComponent {
}
class
_DialState
extends
State
<
_Dial
>
{
double
_theta
;
void
initState
()
{
super
.
initState
();
_theta
=
_getThetaForTime
(
config
.
selectedTime
);
_theta
=
new
ValuePerformance
(
variable:
new
AnimatedValue
<
double
>(
_getThetaForTime
(
config
.
selectedTime
),
curve:
Curves
.
ease
),
duration:
_kDialAnimateDuration
)..
addListener
(()
=>
setState
(()
{
}));
}
void
didUpdateConfig
(
_Dial
oldConfig
)
{
if
(
config
.
mode
!=
oldConfig
.
mode
)
_theta
=
_getThetaForTime
(
config
.
selectedTime
);
if
(
config
.
mode
!=
oldConfig
.
mode
&&
!
_dragging
)
_animateTo
(
_getThetaForTime
(
config
.
selectedTime
));
}
ValuePerformance
<
double
>
_theta
;
bool
_dragging
=
false
;
static
double
_nearest
(
double
target
,
double
a
,
double
b
)
{
return
((
target
-
a
).
abs
()
<
(
target
-
b
).
abs
())
?
a
:
b
;
}
void
_animateTo
(
double
targetTheta
)
{
double
currentTheta
=
_theta
.
value
;
double
beginTheta
=
_nearest
(
targetTheta
,
currentTheta
,
currentTheta
+
_kTwoPi
);
beginTheta
=
_nearest
(
targetTheta
,
beginTheta
,
currentTheta
-
_kTwoPi
);
_theta
..
variable
.
begin
=
beginTheta
..
variable
.
end
=
targetTheta
..
progress
=
0.0
..
play
();
}
double
_getThetaForTime
(
TimeOfDay
time
)
{
double
fraction
=
(
config
.
mode
==
_TimePickerMode
.
hour
)
?
(
time
.
hour
/
12
)
%
12
:
(
time
.
minute
/
60
)
%
60
;
return
math
.
PI
/
2.0
-
fraction
*
2
*
math
.
PI
;
(
time
.
hour
/
_kHoursPerPeriod
)
%
_kHoursPerPeriod
:
(
time
.
minute
/
_kMinutesPerHour
)
%
_kMinutesPerHour
;
return
(
math
.
PI
/
2.0
-
fraction
*
_kTwoPi
)
%
_kTwoPi
;
}
TimeOfDay
_getTimeForTheta
(
double
theta
)
{
double
fraction
=
(
0.25
-
(
theta
%
(
2
*
math
.
PI
))
/
(
2
*
math
.
PI
)
)
%
1.0
;
double
fraction
=
(
0.25
-
(
theta
%
_kTwoPi
)
/
_kTwoPi
)
%
1.0
;
if
(
config
.
mode
==
_TimePickerMode
.
hour
)
{
int
hourOfPeriod
=
(
fraction
*
_kHoursPerPeriod
).
round
()
%
_kHoursPerPeriod
;
return
config
.
selectedTime
.
replacing
(
hour:
(
fraction
*
12
).
round
()
hour:
hourOfPeriod
+
config
.
selectedTime
.
periodOffset
);
}
else
{
return
config
.
selectedTime
.
replacing
(
minute:
(
fraction
*
60
).
round
()
minute:
(
fraction
*
_kMinutesPerHour
).
round
()
%
_kMinutesPerHour
);
}
}
...
...
@@ -283,7 +385,7 @@ class _DialState extends State<_Dial> {
void
_notifyOnChangedIfNeeded
()
{
if
(
config
.
onChanged
==
null
)
return
;
TimeOfDay
current
=
_getTimeForTheta
(
_theta
);
TimeOfDay
current
=
_getTimeForTheta
(
_theta
.
value
);
if
(
current
!=
config
.
selectedTime
)
config
.
onChanged
(
current
);
}
...
...
@@ -291,7 +393,7 @@ class _DialState extends State<_Dial> {
void
_updateThetaForPan
()
{
setState
(()
{
Offset
offset
=
_position
-
_center
;
_theta
=
(
math
.
atan2
(
offset
.
dx
,
offset
.
dy
)
-
math
.
PI
/
2.0
)
%
(
2
*
math
.
PI
)
;
_theta
.
variable
.
value
=
(
math
.
atan2
(
offset
.
dx
,
offset
.
dy
)
-
math
.
PI
/
2.0
)
%
_kTwoPi
;
});
}
...
...
@@ -299,6 +401,8 @@ class _DialState extends State<_Dial> {
Point
_center
;
void
_handlePanStart
(
Point
globalPosition
)
{
assert
(!
_dragging
);
_dragging
=
true
;
RenderBox
box
=
context
.
findRenderObject
();
_position
=
box
.
globalToLocal
(
globalPosition
);
double
radius
=
box
.
size
.
shortestSide
/
2.0
;
...
...
@@ -314,14 +418,16 @@ class _DialState extends State<_Dial> {
}
void
_handlePanEnd
(
Offset
velocity
)
{
assert
(
_dragging
);
_dragging
=
false
;
_position
=
null
;
_center
=
null
;
setState
(()
{
// TODO(abarth): Animate to the final value.
_theta
=
_getThetaForTime
(
config
.
selectedTime
);
});
_animateTo
(
_getThetaForTime
(
config
.
selectedTime
));
}
final
List
<
TextPainter
>
_hours
=
_initHours
();
final
List
<
TextPainter
>
_minutes
=
_initMinutes
();
Widget
build
(
BuildContext
context
)
{
return
new
GestureDetector
(
onPanStart:
_handlePanStart
,
...
...
@@ -329,9 +435,9 @@ class _DialState extends State<_Dial> {
onPanEnd:
_handlePanEnd
,
child:
new
CustomPaint
(
painter:
new
_DialPainter
(
labels:
config
.
mode
==
_TimePickerMode
.
hour
?
_
kHours
:
_kM
inutes
,
labels:
config
.
mode
==
_TimePickerMode
.
hour
?
_
hours
:
_m
inutes
,
primaryColor:
Theme
.
of
(
context
).
primaryColor
,
theta:
_theta
theta:
_theta
.
value
)
)
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment