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
1de673bc
Unverified
Commit
1de673bc
authored
Aug 13, 2020
by
Rami
Committed by
GitHub
Aug 13, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Ensure time picker input mode lays out correctly in RTL (#63599)
parent
eb60849e
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
188 additions
and
49 deletions
+188
-49
time_picker.dart
packages/flutter/lib/src/material/time_picker.dart
+113
-48
time_picker_test.dart
...flutter_localizations/test/material/time_picker_test.dart
+75
-1
No files found.
packages/flutter/lib/src/material/time_picker.dart
View file @
1de673bc
...
...
@@ -1414,58 +1414,68 @@ class _TimePickerInputState extends State<_TimePickerInput> {
),
const
SizedBox
(
width:
12.0
),
],
Expanded
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
<
Widget
>[
const
SizedBox
(
height:
8.0
),
_HourMinuteTextField
(
selectedTime:
_selectedTime
,
isHour:
true
,
style:
hourMinuteStyle
,
validator:
_validateHour
,
onSavedSubmitted:
_handleHourSavedSubmitted
,
onChanged:
_handleHourChanged
,
),
const
SizedBox
(
height:
8.0
),
if
(!
hourHasError
&&
!
minuteHasError
)
ExcludeSemantics
(
child:
Text
(
MaterialLocalizations
.
of
(
context
).
timePickerHourLabel
,
style:
theme
.
textTheme
.
caption
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
Expanded
(
child:
Row
(
// Hour/minutes should not change positions in RTL locales.
textDirection:
TextDirection
.
ltr
,
children:
<
Widget
>[
Expanded
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
<
Widget
>[
const
SizedBox
(
height:
8.0
),
_HourTextField
(
selectedTime:
_selectedTime
,
style:
hourMinuteStyle
,
validator:
_validateHour
,
onSavedSubmitted:
_handleHourSavedSubmitted
,
onChanged:
_handleHourChanged
,
),
const
SizedBox
(
height:
8.0
),
if
(!
hourHasError
&&
!
minuteHasError
)
ExcludeSemantics
(
child:
Text
(
MaterialLocalizations
.
of
(
context
).
timePickerHourLabel
,
style:
theme
.
textTheme
.
caption
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
),
),
],
),
),
],
)),
Container
(
margin:
const
EdgeInsets
.
only
(
top:
8.0
),
height:
_kTimePickerHeaderControlHeight
,
child:
_StringFragment
(
timeOfDayFormat:
timeOfDayFormat
),
),
Expanded
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
<
Widget
>[
const
SizedBox
(
height:
8.0
),
_HourMinuteTextField
(
selectedTime:
_selectedTime
,
isHour:
false
,
style:
hourMinuteStyle
,
validator:
_validateMinute
,
onSavedSubmitted:
_handleMinuteSavedSubmitted
,
),
const
SizedBox
(
height:
8.0
),
if
(!
hourHasError
&&
!
minuteHasError
)
ExcludeSemantics
(
child:
Text
(
MaterialLocalizations
.
of
(
context
).
timePickerMinuteLabel
,
style:
theme
.
textTheme
.
caption
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
Container
(
margin:
const
EdgeInsets
.
only
(
top:
8.0
),
height:
_kTimePickerHeaderControlHeight
,
child:
_StringFragment
(
timeOfDayFormat:
timeOfDayFormat
),
),
Expanded
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
<
Widget
>[
const
SizedBox
(
height:
8.0
),
_MinuteTextField
(
selectedTime:
_selectedTime
,
style:
hourMinuteStyle
,
validator:
_validateMinute
,
onSavedSubmitted:
_handleMinuteSavedSubmitted
,
),
const
SizedBox
(
height:
8.0
),
if
(!
hourHasError
&&
!
minuteHasError
)
ExcludeSemantics
(
child:
Text
(
MaterialLocalizations
.
of
(
context
).
timePickerMinuteLabel
,
style:
theme
.
textTheme
.
caption
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
),
),
],
),
),
],
)),
],
),
),
if
(!
use24HourDials
&&
timeOfDayFormat
!=
TimeOfDayFormat
.
a_space_h_colon_mm
)
...<
Widget
>[
const
SizedBox
(
width:
12.0
),
_DayPeriodControl
(
...
...
@@ -1489,6 +1499,61 @@ class _TimePickerInputState extends State<_TimePickerInput> {
}
}
class
_HourTextField
extends
StatelessWidget
{
const
_HourTextField
({
Key
key
,
@required
this
.
selectedTime
,
@required
this
.
style
,
@required
this
.
validator
,
@required
this
.
onSavedSubmitted
,
@required
this
.
onChanged
,
})
:
super
(
key:
key
);
final
TimeOfDay
selectedTime
;
final
TextStyle
style
;
final
FormFieldValidator
<
String
>
validator
;
final
ValueChanged
<
String
>
onSavedSubmitted
;
final
ValueChanged
<
String
>
onChanged
;
@override
Widget
build
(
BuildContext
context
)
{
return
_HourMinuteTextField
(
selectedTime:
selectedTime
,
isHour:
true
,
style:
style
,
validator:
validator
,
onSavedSubmitted:
onSavedSubmitted
,
onChanged:
onChanged
,
);
}
}
class
_MinuteTextField
extends
StatelessWidget
{
const
_MinuteTextField
({
Key
key
,
@required
this
.
selectedTime
,
@required
this
.
style
,
@required
this
.
validator
,
@required
this
.
onSavedSubmitted
,
})
:
super
(
key:
key
);
final
TimeOfDay
selectedTime
;
final
TextStyle
style
;
final
FormFieldValidator
<
String
>
validator
;
final
ValueChanged
<
String
>
onSavedSubmitted
;
@override
Widget
build
(
BuildContext
context
)
{
return
_HourMinuteTextField
(
selectedTime:
selectedTime
,
isHour:
false
,
style:
style
,
validator:
validator
,
onSavedSubmitted:
onSavedSubmitted
,
);
}
}
class
_HourMinuteTextField
extends
StatefulWidget
{
const
_HourMinuteTextField
({
Key
key
,
...
...
packages/flutter_localizations/test/material/time_picker_test.dart
View file @
1de673bc
...
...
@@ -8,10 +8,16 @@ import 'package:flutter_localizations/flutter_localizations.dart';
import
'package:flutter_test/flutter_test.dart'
;
class
_TimePickerLauncher
extends
StatelessWidget
{
const
_TimePickerLauncher
({
Key
key
,
this
.
onChanged
,
this
.
locale
})
:
super
(
key:
key
);
const
_TimePickerLauncher
({
Key
key
,
this
.
onChanged
,
this
.
locale
,
this
.
entryMode
=
TimePickerEntryMode
.
dial
,
})
:
super
(
key:
key
);
final
ValueChanged
<
TimeOfDay
>
onChanged
;
final
Locale
locale
;
final
TimePickerEntryMode
entryMode
;
@override
Widget
build
(
BuildContext
context
)
{
...
...
@@ -28,6 +34,7 @@ class _TimePickerLauncher extends StatelessWidget {
onPressed:
()
async
{
onChanged
(
await
showTimePicker
(
context:
context
,
initialEntryMode:
entryMode
,
initialTime:
const
TimeOfDay
(
hour:
7
,
minute:
0
),
));
},
...
...
@@ -207,6 +214,73 @@ void main() {
tester
.
binding
.
window
.
devicePixelRatioTestValue
=
null
;
});
testWidgets
(
'can localize input mode in all known formats'
,
(
WidgetTester
tester
)
async
{
final
Finder
stringFragmentTextFinder
=
find
.
descendant
(
of:
find
.
byWidgetPredicate
((
Widget
w
)
=>
'
${w.runtimeType}
'
==
'_StringFragment'
),
matching:
find
.
byType
(
Text
),
).
first
;
final
Finder
hourControlFinder
=
find
.
byWidgetPredicate
((
Widget
w
)
=>
'
${w.runtimeType}
'
==
'_HourTextField'
);
final
Finder
minuteControlFinder
=
find
.
byWidgetPredicate
((
Widget
w
)
=>
'
${w.runtimeType}
'
==
'_MinuteTextField'
);
final
Finder
dayPeriodControlFinder
=
find
.
byWidgetPredicate
((
Widget
w
)
=>
'
${w.runtimeType}
'
==
'_DayPeriodControl'
);
// TODO(yjbanov): also test `HH.mm` (in_ID), `a h:mm` (ko_KR) and `HH:mm น.` (th_TH) when we have .arb files for them
final
List
<
Locale
>
locales
=
<
Locale
>[
const
Locale
(
'en'
,
'US'
),
//'h:mm a'
const
Locale
(
'en'
,
'GB'
),
//'HH:mm'
const
Locale
(
'es'
,
'ES'
),
//'H:mm'
const
Locale
(
'fr'
,
'CA'
),
//'HH \'h\' mm'
const
Locale
(
'zh'
,
'ZH'
),
//'ah:mm'
const
Locale
(
'fa'
,
'IR'
),
//'H:mm' but RTL
];
for
(
final
Locale
locale
in
locales
)
{
await
tester
.
pumpWidget
(
_TimePickerLauncher
(
onChanged:
(
TimeOfDay
time
)
{
},
locale:
locale
,
entryMode:
TimePickerEntryMode
.
input
));
await
tester
.
tap
(
find
.
text
(
'X'
));
await
tester
.
pumpAndSettle
(
const
Duration
(
seconds:
1
));
final
Text
stringFragmentText
=
tester
.
widget
(
stringFragmentTextFinder
);
final
double
hourLeftOffset
=
tester
.
getTopLeft
(
hourControlFinder
).
dx
;
final
double
minuteLeftOffset
=
tester
.
getTopLeft
(
minuteControlFinder
).
dx
;
final
double
stringFragmentLeftOffset
=
tester
.
getTopLeft
(
stringFragmentTextFinder
).
dx
;
if
(
locale
==
const
Locale
(
'en'
,
'US'
))
{
final
double
dayPeriodLeftOffset
=
tester
.
getTopLeft
(
dayPeriodControlFinder
).
dx
;
expect
(
stringFragmentText
.
data
,
':'
);
expect
(
hourLeftOffset
,
lessThan
(
stringFragmentLeftOffset
));
expect
(
stringFragmentLeftOffset
,
lessThan
(
minuteLeftOffset
));
expect
(
minuteLeftOffset
,
lessThan
(
dayPeriodLeftOffset
));
}
else
if
(
locale
==
const
Locale
(
'en'
,
'GB'
))
{
expect
(
stringFragmentText
.
data
,
':'
);
expect
(
hourLeftOffset
,
lessThan
(
stringFragmentLeftOffset
));
expect
(
stringFragmentLeftOffset
,
lessThan
(
minuteLeftOffset
));
expect
(
dayPeriodControlFinder
,
findsNothing
);
}
else
if
(
locale
==
const
Locale
(
'es'
,
'ES'
))
{
expect
(
stringFragmentText
.
data
,
':'
);
expect
(
hourLeftOffset
,
lessThan
(
stringFragmentLeftOffset
));
expect
(
stringFragmentLeftOffset
,
lessThan
(
minuteLeftOffset
));
expect
(
dayPeriodControlFinder
,
findsNothing
);
}
else
if
(
locale
==
const
Locale
(
'fr'
,
'CA'
))
{
expect
(
stringFragmentText
.
data
,
'h'
);
expect
(
hourLeftOffset
,
lessThan
(
stringFragmentLeftOffset
));
expect
(
stringFragmentLeftOffset
,
lessThan
(
minuteLeftOffset
));
expect
(
dayPeriodControlFinder
,
findsNothing
);
}
else
if
(
locale
==
const
Locale
(
'zh'
,
'ZH'
))
{
final
double
dayPeriodLeftOffset
=
tester
.
getTopLeft
(
dayPeriodControlFinder
).
dx
;
expect
(
stringFragmentText
.
data
,
':'
);
expect
(
dayPeriodLeftOffset
,
lessThan
(
hourLeftOffset
));
expect
(
hourLeftOffset
,
lessThan
(
stringFragmentLeftOffset
));
expect
(
stringFragmentLeftOffset
,
lessThan
(
minuteLeftOffset
));
}
else
if
(
locale
==
const
Locale
(
'fa'
,
'IR'
))
{
// Even though this is an RTL locale, the hours and minutes positions should remain the same.
expect
(
stringFragmentText
.
data
,
':'
);
expect
(
hourLeftOffset
,
lessThan
(
stringFragmentLeftOffset
));
expect
(
stringFragmentLeftOffset
,
lessThan
(
minuteLeftOffset
));
expect
(
dayPeriodControlFinder
,
findsNothing
);
}
await
finishPicker
(
tester
);
}
});
testWidgets
(
'uses single-ring 24-hour dial for all formats'
,
(
WidgetTester
tester
)
async
{
const
List
<
Locale
>
locales
=
<
Locale
>[
Locale
(
'en'
,
'US'
),
// h
...
...
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