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
0c0ae4a1
Commit
0c0ae4a1
authored
Nov 25, 2015
by
Adam Barth
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #584 from abarth/time_picker
Finish TimePicker
parents
4716b708
72d2706e
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 @
0c0ae4a1
...
...
@@ -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 @
0c0ae4a1
...
...
@@ -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 @
0c0ae4a1
...
...
@@ -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