Unverified Commit d10e46ea authored by Matt Sullivan's avatar Matt Sullivan Committed by GitHub

Chevrons in month picker are semi-transparent when the month is scrolled (#19363)

* Chevrons in month picker are semi-transparent when the month is scrolled

* Added type missing annotation
parent 6a8f9041
...@@ -524,7 +524,7 @@ class MonthPicker extends StatefulWidget { ...@@ -524,7 +524,7 @@ class MonthPicker extends StatefulWidget {
_MonthPickerState createState() => new _MonthPickerState(); _MonthPickerState createState() => new _MonthPickerState();
} }
class _MonthPickerState extends State<MonthPicker> { class _MonthPickerState extends State<MonthPicker> with SingleTickerProviderStateMixin {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
...@@ -533,6 +533,17 @@ class _MonthPickerState extends State<MonthPicker> { ...@@ -533,6 +533,17 @@ class _MonthPickerState extends State<MonthPicker> {
_dayPickerController = new PageController(initialPage: monthPage); _dayPickerController = new PageController(initialPage: monthPage);
_handleMonthPageChanged(monthPage); _handleMonthPageChanged(monthPage);
_updateCurrentDate(); _updateCurrentDate();
// Setup the fade animation for chevrons
_chevronOpacityController = new AnimationController(
duration: const Duration(milliseconds: 500), vsync: this
);
_chevronOpacityAnimation = new Tween<double>(begin: 1.0, end: 0.5).animate(
new CurvedAnimation(
parent: _chevronOpacityController,
curve: Curves.easeInOut,
)
);
} }
@override @override
...@@ -559,6 +570,8 @@ class _MonthPickerState extends State<MonthPicker> { ...@@ -559,6 +570,8 @@ class _MonthPickerState extends State<MonthPicker> {
DateTime _currentDisplayedMonthDate; DateTime _currentDisplayedMonthDate;
Timer _timer; Timer _timer;
PageController _dayPickerController; PageController _dayPickerController;
AnimationController _chevronOpacityController;
Animation<double> _chevronOpacityAnimation;
void _updateCurrentDate() { void _updateCurrentDate() {
_todayDate = new DateTime.now(); _todayDate = new DateTime.now();
...@@ -642,13 +655,25 @@ class _MonthPickerState extends State<MonthPicker> { ...@@ -642,13 +655,25 @@ class _MonthPickerState extends State<MonthPicker> {
children: <Widget>[ children: <Widget>[
new Semantics( new Semantics(
sortKey: _MonthPickerSortKey.calendar, sortKey: _MonthPickerSortKey.calendar,
child: new PageView.builder( child: new NotificationListener<ScrollStartNotification>(
key: new ValueKey<DateTime>(widget.selectedDate), onNotification: (_) {
controller: _dayPickerController, _chevronOpacityController.forward();
scrollDirection: Axis.horizontal, return false;
itemCount: _monthDelta(widget.firstDate, widget.lastDate) + 1, },
itemBuilder: _buildItems, child: new NotificationListener<ScrollEndNotification>(
onPageChanged: _handleMonthPageChanged, onNotification: (_) {
_chevronOpacityController.reverse();
return false;
},
child: new PageView.builder(
key: new ValueKey<DateTime>(widget.selectedDate),
controller: _dayPickerController,
scrollDirection: Axis.horizontal,
itemCount: _monthDelta(widget.firstDate, widget.lastDate) + 1,
itemBuilder: _buildItems,
onPageChanged: _handleMonthPageChanged,
),
),
), ),
), ),
new PositionedDirectional( new PositionedDirectional(
...@@ -656,10 +681,13 @@ class _MonthPickerState extends State<MonthPicker> { ...@@ -656,10 +681,13 @@ class _MonthPickerState extends State<MonthPicker> {
start: 8.0, start: 8.0,
child: new Semantics( child: new Semantics(
sortKey: _MonthPickerSortKey.previousMonth, sortKey: _MonthPickerSortKey.previousMonth,
child: new IconButton( child: new FadeTransition(
icon: const Icon(Icons.chevron_left), opacity: _chevronOpacityAnimation,
tooltip: _isDisplayingFirstMonth ? null : '${localizations.previousMonthTooltip} ${localizations.formatMonthYear(_previousMonthDate)}', child: new IconButton(
onPressed: _isDisplayingFirstMonth ? null : _handlePreviousMonth, icon: const Icon(Icons.chevron_left),
tooltip: _isDisplayingFirstMonth ? null : '${localizations.previousMonthTooltip} ${localizations.formatMonthYear(_previousMonthDate)}',
onPressed: _isDisplayingFirstMonth ? null : _handlePreviousMonth,
),
), ),
), ),
), ),
...@@ -668,10 +696,13 @@ class _MonthPickerState extends State<MonthPicker> { ...@@ -668,10 +696,13 @@ class _MonthPickerState extends State<MonthPicker> {
end: 8.0, end: 8.0,
child: new Semantics( child: new Semantics(
sortKey: _MonthPickerSortKey.nextMonth, sortKey: _MonthPickerSortKey.nextMonth,
child: new IconButton( child: new FadeTransition(
icon: const Icon(Icons.chevron_right), opacity: _chevronOpacityAnimation,
tooltip: _isDisplayingLastMonth ? null : '${localizations.nextMonthTooltip} ${localizations.formatMonthYear(_nextMonthDate)}', child: new IconButton(
onPressed: _isDisplayingLastMonth ? null : _handleNextMonth, icon: const Icon(Icons.chevron_right),
tooltip: _isDisplayingLastMonth ? null : '${localizations.nextMonthTooltip} ${localizations.formatMonthYear(_nextMonthDate)}',
onPressed: _isDisplayingLastMonth ? null : _handleNextMonth,
),
), ),
), ),
), ),
......
...@@ -622,4 +622,65 @@ void _tests() { ...@@ -622,4 +622,65 @@ void _tests() {
semantics.dispose(); semantics.dispose();
}); });
testWidgets('chervons animate when scrolling month picker', (WidgetTester tester) async {
final Key _datePickerKey = new UniqueKey();
DateTime _selectedDate = new DateTime(2016, DateTime.july, 26);
await tester.pumpWidget(
new MaterialApp(
home: new StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return new Container(
width: 400.0,
child: new SingleChildScrollView(
child: new Material(
child: new MonthPicker(
firstDate: new DateTime(0),
lastDate: new DateTime(9999),
key: _datePickerKey,
selectedDate: _selectedDate,
onChanged: (DateTime value) {
setState(() {
_selectedDate = value;
});
},
),
),
),
);
},
),
)
);
final Finder chevronFinder = find.byType(IconButton);
final List<RenderAnimatedOpacity> chevronRenderers = chevronFinder.evaluate().map(
(Element element) => element.ancestorRenderObjectOfType(
const TypeMatcher<RenderAnimatedOpacity>())).cast<RenderAnimatedOpacity>().toList();
// Initial chevron animation state should be dismissed
// An AlwaysStoppedAnimation is also found and is ignored
for(RenderAnimatedOpacity renderer in chevronRenderers) {
expect(renderer.opacity.value, equals(1.0));
expect(renderer.opacity.status, equals(AnimationStatus.dismissed));
}
// Drag and hold the picker to test for the opacity change
final TestGesture gesture = await tester.startGesture(const Offset(100.0, 100.0));
await gesture.moveBy(const Offset(50.0, 100.0));
await tester.pumpAndSettle();
for(RenderAnimatedOpacity renderer in chevronRenderers) {
expect(renderer.opacity.value, equals(0.5));
expect(renderer.opacity.status, equals(AnimationStatus.completed));
}
// Release the drag and test for the opacity to return to original value
await gesture.up();
await tester.pumpAndSettle();
for(RenderAnimatedOpacity renderer in chevronRenderers) {
expect(renderer.opacity.value, equals(1.0));
expect(renderer.opacity.status, equals(AnimationStatus.dismissed));
}
});
} }
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