diff --git a/examples/material_gallery/lib/main.dart b/examples/material_gallery/lib/main.dart index de23b7fa2f..f7f510212b 100644 --- a/examples/material_gallery/lib/main.dart +++ b/examples/material_gallery/lib/main.dart @@ -5,14 +5,16 @@ import 'package:flutter/material.dart'; import 'chip_demo.dart'; -import 'gallery_page.dart'; import 'date_picker_demo.dart'; +import 'gallery_page.dart'; +import 'time_picker_demo.dart'; import 'widget_demo.dart'; import 'drop_down_demo.dart'; final List _kDemos = [ kChipDemo, kDatePickerDemo, + kTimePickerDemo, kDropDownDemo, ]; diff --git a/examples/material_gallery/lib/time_picker_demo.dart b/examples/material_gallery/lib/time_picker_demo.dart new file mode 100644 index 0000000000..bd24d6c77e --- /dev/null +++ b/examples/material_gallery/lib/time_picker_demo.dart @@ -0,0 +1,45 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/material.dart'; + +import 'widget_demo.dart'; + +class TimePickerDemo extends StatefulComponent { + _TimePickerDemoState createState() => new _TimePickerDemoState(); +} + +class _TimePickerDemoState extends State { + TimeOfDay _selectedTime = const TimeOfDay(hour: 7, minute: 28); + + Future _handleSelectTime() async { + TimeOfDay picked = await showTimePicker( + context: context, + initialTime: _selectedTime + ); + if (picked != _selectedTime) { + setState(() { + _selectedTime = picked; + }); + } + } + + Widget build(BuildContext context) { + return new Column([ + new Text('${_selectedTime.hour}:${_selectedTime.minute}'), + new RaisedButton( + onPressed: _handleSelectTime, + child: new Text('SELECT TIME') + ), + ], justifyContent: FlexJustifyContent.center); + } +} + +final WidgetDemo kTimePickerDemo = new WidgetDemo( + title: 'Time Picker', + routeName: '/time-picker', + builder: (_) => new TimePickerDemo() +); diff --git a/packages/flutter/lib/material.dart b/packages/flutter/lib/material.dart index 6e846051ac..0453f7c67d 100644 --- a/packages/flutter/lib/material.dart +++ b/packages/flutter/lib/material.dart @@ -50,6 +50,8 @@ export 'src/material/switch.dart'; export 'src/material/tabs.dart'; export 'src/material/theme.dart'; export 'src/material/theme_data.dart'; +export 'src/material/time_picker.dart'; +export 'src/material/time_picker_dialog.dart'; export 'src/material/title.dart'; export 'src/material/tool_bar.dart'; export 'src/material/typography.dart'; diff --git a/packages/flutter/lib/src/material/date_picker.dart b/packages/flutter/lib/src/material/date_picker.dart index abea0ba77f..38c0e54984 100644 --- a/packages/flutter/lib/src/material/date_picker.dart +++ b/packages/flutter/lib/src/material/date_picker.dart @@ -14,9 +14,7 @@ import 'ink_well.dart'; import 'theme.dart'; import 'typography.dart'; -enum DatePickerMode { day, year } - -typedef void DatePickerModeChanged(DatePickerMode value); +enum _DatePickerMode { day, year } class DatePicker extends StatefulComponent { DatePicker({ @@ -39,9 +37,9 @@ class DatePicker extends StatefulComponent { } class _DatePickerState extends State { - DatePickerMode _mode = DatePickerMode.day; + _DatePickerMode _mode = _DatePickerMode.day; - void _handleModeChanged(DatePickerMode mode) { + void _handleModeChanged(_DatePickerMode mode) { userFeedback.performHapticFeedback(HapticFeedbackType.VIRTUAL_KEY); setState(() { _mode = mode; @@ -51,7 +49,7 @@ class _DatePickerState extends State { void _handleYearChanged(DateTime dateTime) { userFeedback.performHapticFeedback(HapticFeedbackType.VIRTUAL_KEY); setState(() { - _mode = DatePickerMode.day; + _mode = _DatePickerMode.day; }); if (config.onChanged != null) config.onChanged(dateTime); @@ -73,7 +71,7 @@ class _DatePickerState extends State { ); Widget picker; switch (_mode) { - case DatePickerMode.day: + case _DatePickerMode.day: picker = new MonthPicker( selectedDate: config.selectedDate, onChanged: _handleDayChanged, @@ -82,7 +80,7 @@ class _DatePickerState extends State { itemExtent: _calendarHeight ); break; - case DatePickerMode.year: + case _DatePickerMode.year: picker = new YearPicker( selectedDate: config.selectedDate, onChanged: _handleYearChanged, @@ -110,10 +108,10 @@ class _DatePickerHeader extends StatelessComponent { } DateTime selectedDate; - DatePickerMode mode; - DatePickerModeChanged onModeChanged; + _DatePickerMode mode; + ValueChanged<_DatePickerMode> onModeChanged; - void _handleChangeMode(DatePickerMode value) { + void _handleChangeMode(_DatePickerMode value) { if (value != mode) onModeChanged(value); } @@ -125,12 +123,12 @@ class _DatePickerHeader extends StatelessComponent { Color yearColor; switch(theme.primaryColorBrightness) { case ThemeBrightness.light: - dayColor = mode == DatePickerMode.day ? Colors.black87 : Colors.black54; - yearColor = mode == DatePickerMode.year ? Colors.black87 : Colors.black54; + dayColor = mode == _DatePickerMode.day ? Colors.black87 : Colors.black54; + yearColor = mode == _DatePickerMode.year ? Colors.black87 : Colors.black54; break; case ThemeBrightness.dark: - dayColor = mode == DatePickerMode.day ? Colors.white : Colors.white70; - yearColor = mode == DatePickerMode.year ? Colors.white : Colors.white70; + dayColor = mode == _DatePickerMode.day ? Colors.white : Colors.white70; + yearColor = mode == _DatePickerMode.year ? Colors.white : Colors.white70; break; } TextStyle dayStyle = headerTheme.display3.copyWith(color: dayColor, height: 1.0, fontSize: 100.0); @@ -142,15 +140,15 @@ class _DatePickerHeader extends StatelessComponent { decoration: new BoxDecoration(backgroundColor: theme.primaryColor), child: new Column([ new GestureDetector( - onTap: () => _handleChangeMode(DatePickerMode.day), + onTap: () => _handleChangeMode(_DatePickerMode.day), child: new Text(new DateFormat("MMM").format(selectedDate).toUpperCase(), style: monthStyle) ), new GestureDetector( - onTap: () => _handleChangeMode(DatePickerMode.day), + onTap: () => _handleChangeMode(_DatePickerMode.day), child: new Text(new DateFormat("d").format(selectedDate), style: dayStyle) ), new GestureDetector( - onTap: () => _handleChangeMode(DatePickerMode.year), + onTap: () => _handleChangeMode(_DatePickerMode.year), child: new Text(new DateFormat("yyyy").format(selectedDate), style: yearStyle) ) ]) diff --git a/packages/flutter/lib/src/material/time_picker.dart b/packages/flutter/lib/src/material/time_picker.dart new file mode 100644 index 0000000000..71e1b0dd7a --- /dev/null +++ b/packages/flutter/lib/src/material/time_picker.dart @@ -0,0 +1,125 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; + +import 'colors.dart'; +import 'theme.dart'; +import 'typography.dart'; + +class TimeOfDay { + const TimeOfDay({ this.hour, this.minute }); + + /// The selected hour, in 24 hour time from 0..23 + final int hour; + + /// The selected minute. + final int minute; +} + +enum _TimePickerMode { hour, minute } + +class TimePicker extends StatefulComponent { + TimePicker({ + this.selectedTime, + this.onChanged + }) { + assert(selectedTime != null); + } + + final TimeOfDay selectedTime; + final ValueChanged onChanged; + + _TimePickerState createState() => new _TimePickerState(); +} + +class _TimePickerState extends State { + _TimePickerMode _mode = _TimePickerMode.hour; + + void _handleModeChanged(_TimePickerMode mode) { + userFeedback.performHapticFeedback(HapticFeedbackType.VIRTUAL_KEY); + setState(() { + _mode = mode; + }); + } + + Widget build(BuildContext context) { + Widget header = new _TimePickerHeader( + selectedTime: config.selectedTime, + mode: _mode, + onModeChanged: _handleModeChanged + ); + return new Column([ + header, + new AspectRatio( + aspectRatio: 1.0, + child: new Container( + margin: const EdgeDims.all(12.0), + decoration: new BoxDecoration( + backgroundColor: Colors.grey[300], + shape: Shape.circle + ) + ) + ) + ], alignItems: FlexAlignItems.stretch); + } + +} + +// Shows the selected date in large font and toggles between year and day mode +class _TimePickerHeader extends StatelessComponent { + _TimePickerHeader({ this.selectedTime, this.mode, this.onModeChanged }) { + assert(selectedTime != null); + assert(mode != null); + } + + TimeOfDay selectedTime; + _TimePickerMode mode; + ValueChanged<_TimePickerMode> onModeChanged; + + void _handleChangeMode(_TimePickerMode value) { + if (value != mode) + onModeChanged(value); + } + + Widget build(BuildContext context) { + ThemeData theme = Theme.of(context); + TextTheme headerTheme = theme.primaryTextTheme; + + Color activeColor; + Color inactiveColor; + switch(theme.primaryColorBrightness) { + case ThemeBrightness.light: + activeColor = Colors.black87; + inactiveColor = Colors.black54; + break; + case ThemeBrightness.dark: + activeColor = Colors.white; + inactiveColor = Colors.white70; + break; + } + TextStyle activeStyle = headerTheme.display3.copyWith(color: activeColor, height: 1.0); + TextStyle inactiveStyle = headerTheme.display3.copyWith(color: inactiveColor, height: 1.0); + + TextStyle hourStyle = mode == _TimePickerMode.hour ? activeStyle : inactiveStyle; + TextStyle minuteStyle = mode == _TimePickerMode.minute ? activeStyle : inactiveStyle; + + return new Container( + padding: new EdgeDims.all(10.0), + decoration: new BoxDecoration(backgroundColor: theme.primaryColor), + child: new Row([ + new GestureDetector( + onTap: () => _handleChangeMode(_TimePickerMode.hour), + child: new Text(selectedTime.hour.toString(), style: hourStyle) + ), + new Text(':', style: inactiveStyle), + new GestureDetector( + onTap: () => _handleChangeMode(_TimePickerMode.minute), + child: new Text(selectedTime.minute.toString(), style: minuteStyle) + ), + ], justifyContent: FlexJustifyContent.end) + ); + } +} diff --git a/packages/flutter/lib/src/material/time_picker_dialog.dart b/packages/flutter/lib/src/material/time_picker_dialog.dart new file mode 100644 index 0000000000..54dabb8e8e --- /dev/null +++ b/packages/flutter/lib/src/material/time_picker_dialog.dart @@ -0,0 +1,68 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/widgets.dart'; + +import 'dialog.dart'; +import 'time_picker.dart'; +import 'flat_button.dart'; + +class _TimePickerDialog extends StatefulComponent { + _TimePickerDialog({ + Key key, + this.initialTime + }) : super(key: key); + + final TimeOfDay initialTime; + + _TimePickerDialogState createState() => new _TimePickerDialogState(); +} + +class _TimePickerDialogState extends State<_TimePickerDialog> { + void initState() { + super.initState(); + _selectedTime = config.initialTime; + } + + TimeOfDay _selectedTime; + + void _handleCancel() { + Navigator.of(context).pop(); + } + + void _handleOk() { + Navigator.of(context).pop(_selectedTime); + } + + Widget build(BuildContext context) { + return new Dialog( + content: new TimePicker( + selectedTime: _selectedTime + ), + contentPadding: EdgeDims.zero, + actions: [ + new FlatButton( + child: new Text('CANCEL'), + onPressed: _handleCancel + ), + new FlatButton( + child: new Text('OK'), + onPressed: _handleOk + ), + ] + ); + } +} + +Future showTimePicker({ + BuildContext context, + TimeOfDay initialTime +}) async { + return await showDialog( + context: context, + child: new _TimePickerDialog(initialTime: initialTime) + ) ?? initialTime; +}