mirror of
https://github.com/encounter/engine.git
synced 2026-03-30 11:09:55 -07:00
Add date picker to widgets library and teach fitness app to use it
Also, add an example for the date picker
This commit is contained in:
@@ -13,6 +13,12 @@ class DateUtils {
|
||||
static const MS_IN_WEEK =
|
||||
DateTime.DAYS_PER_WEEK * Duration.MILLISECONDS_PER_DAY;
|
||||
|
||||
// TODO(jmesserly): locale specific date format
|
||||
static String _twoDigits(int n) {
|
||||
if (n >= 10) return "${n}";
|
||||
return "0${n}";
|
||||
}
|
||||
|
||||
/** Formats a time in H:MM A format */
|
||||
static String toHourMinutesString(Duration duration) {
|
||||
assert(duration.inDays == 0);
|
||||
@@ -68,14 +74,16 @@ class DateUtils {
|
||||
} else if (delta.inMilliseconds < MS_IN_WEEK) {
|
||||
return WEEKDAYS[then.weekday];
|
||||
} else {
|
||||
// TODO(jmesserly): locale specific date format
|
||||
String twoDigits(int n) {
|
||||
if (n >= 10) return "${n}";
|
||||
return "0${n}";
|
||||
}
|
||||
String twoDigitMonth = twoDigits(then.month);
|
||||
String twoDigitDay = twoDigits(then.day);
|
||||
String twoDigitMonth = _twoDigits(then.month);
|
||||
String twoDigitDay = _twoDigits(then.day);
|
||||
return "${then.year}-${twoDigitMonth}-${twoDigitDay}";
|
||||
}
|
||||
}
|
||||
|
||||
static String toDateString(DateTime then) {
|
||||
// TODO(jmesserly): locale specific date format
|
||||
String twoDigitMonth = _twoDigits(then.month);
|
||||
String twoDigitDay = _twoDigits(then.day);
|
||||
return "${then.year}-${twoDigitMonth}-${twoDigitDay}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ abstract class FitnessItem {
|
||||
Map toJson() => { 'when' : when.toIso8601String() };
|
||||
|
||||
// TODO(jackson): Internationalize
|
||||
String get displayDate => DateUtils.toRecentTimeString(when);
|
||||
String get displayDate => DateUtils.toDateString(when);
|
||||
|
||||
FitnessItemRow toRow({ FitnessItemHandler onDismissed });
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ class Measurement extends FitnessItem {
|
||||
Map toJson() {
|
||||
Map json = super.toJson();
|
||||
json['weight'] = weight;
|
||||
json['type'] = runtimeType.toString();
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -53,6 +54,55 @@ class MeasurementRow extends FitnessItemRow {
|
||||
}
|
||||
}
|
||||
|
||||
class MeasurementDateDialog extends StatefulComponent {
|
||||
MeasurementDateDialog({ this.navigator, this.previousDate });
|
||||
|
||||
Navigator navigator;
|
||||
DateTime previousDate;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_selectedDate = previousDate;
|
||||
}
|
||||
|
||||
void syncConstructorArguments(MeasurementDateDialog source) {
|
||||
navigator = source.navigator;
|
||||
previousDate = source.previousDate;
|
||||
}
|
||||
|
||||
DateTime _selectedDate;
|
||||
|
||||
void _handleDateChanged(DateTime value) {
|
||||
setState(() {
|
||||
_selectedDate = value;
|
||||
});
|
||||
}
|
||||
|
||||
Widget build() {
|
||||
return new Dialog(
|
||||
content: new DatePicker(
|
||||
selectedDate: _selectedDate,
|
||||
firstDate: new DateTime(2015, 8),
|
||||
lastDate: new DateTime(2101),
|
||||
onChanged: _handleDateChanged
|
||||
),
|
||||
contentPadding: EdgeDims.zero,
|
||||
actions: [
|
||||
new FlatButton(
|
||||
child: new Text('CANCEL'),
|
||||
onPressed: navigator.pop
|
||||
),
|
||||
new FlatButton(
|
||||
child: new Text('OK'),
|
||||
onPressed: () {
|
||||
navigator.pop(_selectedDate);
|
||||
}
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MeasurementFragment extends StatefulComponent {
|
||||
|
||||
MeasurementFragment({ this.navigator, this.onCreated });
|
||||
@@ -66,6 +116,7 @@ class MeasurementFragment extends StatefulComponent {
|
||||
}
|
||||
|
||||
String _weight = "";
|
||||
DateTime _when = new DateTime.now();
|
||||
String _errorMessage = null;
|
||||
|
||||
EventDisposition _handleSave() {
|
||||
@@ -79,7 +130,7 @@ class MeasurementFragment extends StatefulComponent {
|
||||
});
|
||||
return EventDisposition.processed;
|
||||
}
|
||||
onCreated(new Measurement(when: new DateTime.now(), weight: parsedWeight));
|
||||
onCreated(new Measurement(when: _when, weight: parsedWeight));
|
||||
navigator.pop();
|
||||
return EventDisposition.processed;
|
||||
}
|
||||
@@ -107,23 +158,44 @@ class MeasurementFragment extends StatefulComponent {
|
||||
|
||||
static final GlobalKey weightKey = new GlobalKey();
|
||||
|
||||
EventDisposition _handleDatePressed(_) {
|
||||
showDialog(navigator, (navigator) {
|
||||
return new MeasurementDateDialog(navigator: navigator, previousDate: _when);
|
||||
}).then((DateTime value) {
|
||||
if (value == null)
|
||||
return;
|
||||
setState(() {
|
||||
_when = value;
|
||||
});
|
||||
});
|
||||
return EventDisposition.processed;
|
||||
}
|
||||
|
||||
Widget buildBody() {
|
||||
Measurement measurement = new Measurement(when: new DateTime.now());
|
||||
Measurement measurement = new Measurement(when: _when);
|
||||
// TODO(jackson): Revisit the layout of this pane to be more maintainable
|
||||
return new Material(
|
||||
type: MaterialType.canvas,
|
||||
child: new ScrollableViewport(
|
||||
child: new Container(
|
||||
padding: const EdgeDims.all(20.0),
|
||||
child: new BlockBody([
|
||||
new Text(measurement.displayDate),
|
||||
new Input(
|
||||
key: weightKey,
|
||||
placeholder: 'Enter weight',
|
||||
keyboardType: KeyboardType_NUMBER,
|
||||
onChanged: _handleWeightChanged
|
||||
),
|
||||
])
|
||||
)
|
||||
child: new Container(
|
||||
padding: const EdgeDims.all(20.0),
|
||||
child: new Column([
|
||||
new Listener(
|
||||
onGestureTap: _handleDatePressed,
|
||||
child: new Container(
|
||||
height: 50.0,
|
||||
child: new Column([
|
||||
new Text('Measurement Date'),
|
||||
new Text(measurement.displayDate, style: Theme.of(this).text.caption),
|
||||
], alignItems: FlexAlignItems.start)
|
||||
)
|
||||
),
|
||||
new Input(
|
||||
key: weightKey,
|
||||
placeholder: 'Enter weight',
|
||||
keyboardType: KeyboardType_NUMBER,
|
||||
onChanged: _handleWeightChanged
|
||||
),
|
||||
], alignItems: FlexAlignItems.stretch)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
// 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:sky/widgets.dart';
|
||||
import 'package:sky/theme/colors.dart' as colors;
|
||||
|
||||
void main() => runApp(new DatePickerDemo());
|
||||
|
||||
class DatePickerDemo extends App {
|
||||
|
||||
DateTime _dateTime;
|
||||
|
||||
void initState() {
|
||||
DateTime now = new DateTime.now();
|
||||
_dateTime = new DateTime(now.year, now.month, now.day);
|
||||
}
|
||||
|
||||
void _handleDateChanged(DateTime dateTime) {
|
||||
setState(() {
|
||||
_dateTime = dateTime;
|
||||
});
|
||||
}
|
||||
|
||||
Widget build() {
|
||||
return new Theme(
|
||||
data: new ThemeData(
|
||||
brightness: ThemeBrightness.light,
|
||||
primarySwatch: colors.Teal
|
||||
),
|
||||
child: new Stack([
|
||||
new Scaffold(
|
||||
toolbar: new ToolBar(center: new Text("Date Picker")),
|
||||
body: new Material(
|
||||
child: new Row(
|
||||
[new Text(_dateTime.toString())],
|
||||
alignItems: FlexAlignItems.end,
|
||||
justifyContent: FlexJustifyContent.center
|
||||
)
|
||||
)
|
||||
),
|
||||
new Dialog(
|
||||
content: new DatePicker(
|
||||
selectedDate: _dateTime,
|
||||
firstDate: new DateTime(2015, 8),
|
||||
lastDate: new DateTime(2101),
|
||||
onChanged: _handleDateChanged
|
||||
),
|
||||
contentPadding: EdgeDims.zero,
|
||||
actions: [
|
||||
new FlatButton(
|
||||
child: new Text('CANCEL')
|
||||
),
|
||||
new FlatButton(
|
||||
child: new Text('OK')
|
||||
),
|
||||
]
|
||||
)
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ export 'widgets/basic.dart';
|
||||
export 'widgets/button_base.dart';
|
||||
export 'widgets/card.dart';
|
||||
export 'widgets/checkbox.dart';
|
||||
export 'widgets/date_picker.dart';
|
||||
export 'widgets/default_text_style.dart';
|
||||
export 'widgets/dialog.dart';
|
||||
export 'widgets/dismissable.dart';
|
||||
|
||||
@@ -0,0 +1,393 @@
|
||||
// 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:sky/theme/colors.dart' as colors;
|
||||
import 'package:sky/theme/typography.dart' as typography;
|
||||
import 'package:sky/widgets.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:intl/date_symbols.dart';
|
||||
|
||||
typedef void DatePickerValueChanged(DateTime dateTime);
|
||||
|
||||
enum DatePickerMode { day, year }
|
||||
|
||||
typedef void DatePickerModeChanged(DatePickerMode value);
|
||||
|
||||
class DatePicker extends StatefulComponent {
|
||||
DatePicker({
|
||||
this.selectedDate,
|
||||
this.onChanged,
|
||||
this.firstDate,
|
||||
this.lastDate
|
||||
}) {
|
||||
assert(selectedDate != null);
|
||||
assert(firstDate != null);
|
||||
assert(lastDate != null);
|
||||
}
|
||||
|
||||
DateTime selectedDate;
|
||||
DatePickerValueChanged onChanged;
|
||||
DateTime firstDate;
|
||||
DateTime lastDate;
|
||||
|
||||
void syncConstructorArguments(DatePicker source) {
|
||||
selectedDate = source.selectedDate;
|
||||
onChanged = source.onChanged;
|
||||
firstDate = source.firstDate;
|
||||
lastDate = source.lastDate;
|
||||
}
|
||||
|
||||
DatePickerMode _mode = DatePickerMode.day;
|
||||
|
||||
void _handleModeChanged(DatePickerMode mode) {
|
||||
setState(() {
|
||||
_mode = mode;
|
||||
});
|
||||
}
|
||||
|
||||
void _handleYearChanged(DateTime dateTime) {
|
||||
setState(() {
|
||||
_mode = DatePickerMode.day;
|
||||
});
|
||||
if (onChanged != null)
|
||||
onChanged(dateTime);
|
||||
}
|
||||
|
||||
static const double _calendarHeight = 210.0;
|
||||
|
||||
Widget build() {
|
||||
Widget header = new DatePickerHeader(
|
||||
selectedDate: selectedDate,
|
||||
mode: _mode,
|
||||
onModeChanged: _handleModeChanged
|
||||
);
|
||||
Widget picker;
|
||||
switch (_mode) {
|
||||
case DatePickerMode.day:
|
||||
picker = new MonthPicker(
|
||||
selectedDate: selectedDate,
|
||||
onChanged: onChanged,
|
||||
firstDate: firstDate,
|
||||
lastDate: lastDate,
|
||||
itemExtent: _calendarHeight
|
||||
);
|
||||
break;
|
||||
case DatePickerMode.year:
|
||||
picker = new YearPicker(
|
||||
selectedDate: selectedDate,
|
||||
onChanged: _handleYearChanged,
|
||||
firstDate: firstDate,
|
||||
lastDate: lastDate
|
||||
);
|
||||
break;
|
||||
}
|
||||
return new BlockBody([header, new Container(height: _calendarHeight, child: picker)]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Shows the selected date in large font and toggles between year and day mode
|
||||
class DatePickerHeader extends Component {
|
||||
DatePickerHeader({ this.selectedDate, this.mode, this.onModeChanged }) {
|
||||
assert(selectedDate != null);
|
||||
assert(mode != null);
|
||||
}
|
||||
|
||||
DateTime selectedDate;
|
||||
DatePickerMode mode;
|
||||
DatePickerModeChanged onModeChanged;
|
||||
|
||||
EventDisposition _handleChangeMode(DatePickerMode value) {
|
||||
if (value == mode)
|
||||
return EventDisposition.ignored;
|
||||
onModeChanged(value);
|
||||
return EventDisposition.processed;
|
||||
}
|
||||
|
||||
Widget build() {
|
||||
ThemeData theme = Theme.of(this);
|
||||
typography.TextTheme headerTheme;
|
||||
Color dayColor;
|
||||
Color yearColor;
|
||||
switch(theme.primaryColorBrightness) {
|
||||
case ThemeBrightness.light:
|
||||
headerTheme = typography.black;
|
||||
dayColor = mode == DatePickerMode.day ? colors.black87 : colors.black54;
|
||||
yearColor = mode == DatePickerMode.year ? colors.black87 : colors.black54;
|
||||
break;
|
||||
case ThemeBrightness.dark:
|
||||
headerTheme = typography.white;
|
||||
dayColor = mode == DatePickerMode.day ? colors.white87 : colors.white54;
|
||||
yearColor = mode == DatePickerMode.year ? colors.white87 : colors.white54;
|
||||
break;
|
||||
}
|
||||
TextStyle dayStyle = headerTheme.display3.copyWith(color: dayColor, height: 1.0, fontSize: 100.0);
|
||||
TextStyle monthStyle = headerTheme.headline.copyWith(color: dayColor, height: 1.0);
|
||||
TextStyle yearStyle = headerTheme.headline.copyWith(color: yearColor, height: 1.0);
|
||||
DateTime firstDate = new DateTime(1900);
|
||||
DateTime lastDate = new DateTime(2101);
|
||||
|
||||
return new Container(
|
||||
child: new BlockBody([
|
||||
new Center(
|
||||
child: new Listener(
|
||||
child: new Text(new DateFormat("MMM").format(selectedDate).toUpperCase(), style: monthStyle),
|
||||
onGestureTap: (_) => _handleChangeMode(DatePickerMode.day)
|
||||
)
|
||||
),
|
||||
new Center(
|
||||
child: new Listener(
|
||||
child: new Text(new DateFormat("d").format(selectedDate), style: dayStyle),
|
||||
onGestureTap: (_) => _handleChangeMode(DatePickerMode.day)
|
||||
)
|
||||
),
|
||||
new Center(
|
||||
child: new Listener(
|
||||
child: new Text(new DateFormat("yyyy").format(selectedDate), style: yearStyle),
|
||||
onGestureTap: (_) => _handleChangeMode(DatePickerMode.year)
|
||||
)
|
||||
)
|
||||
]),
|
||||
padding: new EdgeDims.all(10.0),
|
||||
decoration: new BoxDecoration(backgroundColor: theme.primaryColor)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Fixed height component shows a single month and allows choosing a day
|
||||
class DayPicker extends Component {
|
||||
DayPicker({
|
||||
this.selectedDate,
|
||||
this.currentDate,
|
||||
this.onChanged,
|
||||
this.displayedMonth
|
||||
}) {
|
||||
assert(selectedDate != null);
|
||||
assert(currentDate != null);
|
||||
assert(displayedMonth != null);
|
||||
}
|
||||
|
||||
final DateTime selectedDate;
|
||||
final DateTime currentDate;
|
||||
final DatePickerValueChanged onChanged;
|
||||
final DateTime displayedMonth;
|
||||
|
||||
Widget build() {
|
||||
ThemeData theme = Theme.of(this);
|
||||
TextStyle headerStyle = theme.text.caption.copyWith(fontWeight: FontWeight.w700);
|
||||
TextStyle monthStyle = headerStyle.copyWith(fontSize: 14.0, height: 24.0 / 14.0);
|
||||
TextStyle dayStyle = headerStyle.copyWith(fontWeight: FontWeight.w500);
|
||||
DateFormat dateFormat = new DateFormat();
|
||||
DateSymbols symbols = dateFormat.dateSymbols;
|
||||
|
||||
List<Text> headers = [];
|
||||
for (String weekDay in symbols.NARROWWEEKDAYS) {
|
||||
headers.add(new Text(weekDay, style: headerStyle));
|
||||
}
|
||||
List<Widget> rows = [
|
||||
new Text(new DateFormat("MMMM y").format(displayedMonth), style: monthStyle),
|
||||
new Flex(
|
||||
headers,
|
||||
justifyContent: FlexJustifyContent.spaceAround
|
||||
)
|
||||
];
|
||||
int year = displayedMonth.year;
|
||||
int month = displayedMonth.month;
|
||||
// Dart's Date time constructor is very forgiving and will understand
|
||||
// month 13 as January of the next year. :)
|
||||
int daysInMonth = new DateTime(year, month + 1).difference(new DateTime(year, month)).inDays;
|
||||
int firstDay = new DateTime(year, month).day;
|
||||
int weeksShown = 6;
|
||||
List<int> days = [
|
||||
DateTime.SUNDAY,
|
||||
DateTime.MONDAY,
|
||||
DateTime.TUESDAY,
|
||||
DateTime.WEDNESDAY,
|
||||
DateTime.THURSDAY,
|
||||
DateTime.FRIDAY,
|
||||
DateTime.SATURDAY
|
||||
];
|
||||
int daySlots = weeksShown * days.length;
|
||||
List<Widget> labels = [];
|
||||
for (int i = 0; i < daySlots; i++) {
|
||||
// This assumes a start day of SUNDAY, but could be changed.
|
||||
int day = i - firstDay + 1;
|
||||
Widget item;
|
||||
if (day < 1 || day > daysInMonth) {
|
||||
item = new Text("");
|
||||
} else {
|
||||
// Put a light circle around the selected day
|
||||
BoxDecoration decoration = null;
|
||||
if (selectedDate.year == year &&
|
||||
selectedDate.month == month &&
|
||||
selectedDate.day == day)
|
||||
decoration = new BoxDecoration(
|
||||
backgroundColor: theme.primarySwatch[100],
|
||||
shape: Shape.circle
|
||||
);
|
||||
|
||||
// Use a different font color for the current day
|
||||
TextStyle itemStyle = dayStyle;
|
||||
if (currentDate.year == year &&
|
||||
currentDate.month == month &&
|
||||
currentDate.day == day)
|
||||
itemStyle = itemStyle.copyWith(color: theme.primaryColor);
|
||||
|
||||
item = new Listener(
|
||||
onGestureTap: (_) {
|
||||
DateTime result = new DateTime(year, month, day);
|
||||
if (onChanged != null)
|
||||
onChanged(result);
|
||||
},
|
||||
child: new Container(
|
||||
height: 30.0,
|
||||
decoration: decoration,
|
||||
child: new Center(
|
||||
child: new Text(day.toString(), style: itemStyle)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
labels.add(new Flexible(child: item));
|
||||
}
|
||||
for (int w = 0; w < weeksShown; w++) {
|
||||
int startIndex = w * days.length;
|
||||
rows.add(new Container(
|
||||
child: new Flex(
|
||||
labels.sublist(startIndex, startIndex + days.length),
|
||||
justifyContent: FlexJustifyContent.spaceAround
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
return new Column(rows);
|
||||
}
|
||||
}
|
||||
|
||||
// Scrollable list of DayPickers to allow choosing a month
|
||||
class MonthPicker extends ScrollableWidgetList {
|
||||
MonthPicker({
|
||||
this.selectedDate,
|
||||
this.onChanged,
|
||||
this.firstDate,
|
||||
this.lastDate,
|
||||
double itemExtent
|
||||
}) : super(itemExtent: itemExtent) {
|
||||
assert(selectedDate != null);
|
||||
assert(lastDate.isAfter(firstDate));
|
||||
}
|
||||
|
||||
DateTime selectedDate;
|
||||
DatePickerValueChanged onChanged;
|
||||
DateTime firstDate;
|
||||
DateTime lastDate;
|
||||
|
||||
void syncConstructorArguments(MonthPicker source) {
|
||||
selectedDate = source.selectedDate;
|
||||
onChanged = source.onChanged;
|
||||
firstDate = source.firstDate;
|
||||
lastDate = source.lastDate;
|
||||
super.syncConstructorArguments(source);
|
||||
}
|
||||
|
||||
void initState() {
|
||||
_updateCurrentDate();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
DateTime _currentDate;
|
||||
void _updateCurrentDate() {
|
||||
_currentDate = new DateTime.now();
|
||||
DateTime tomorrow = new DateTime(_currentDate.year, _currentDate.month, _currentDate.day + 1);
|
||||
Duration timeUntilTomorrow = tomorrow.difference(_currentDate);
|
||||
timeUntilTomorrow += const Duration(seconds: 1); // so we don't miss it by rounding
|
||||
new Timer(timeUntilTomorrow, () {
|
||||
setState(() {
|
||||
_updateCurrentDate();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
int get itemCount => (lastDate.year - firstDate.year) * 12 + lastDate.month - firstDate.month + 1;
|
||||
|
||||
List<Widget> buildItems(int start, int count) {
|
||||
List<Widget> result = new List<Widget>();
|
||||
DateTime startDate = new DateTime(firstDate.year + start ~/ 12, firstDate.month + start % 12);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
DateTime displayedMonth = new DateTime(startDate.year + i ~/ 12, startDate.month + i % 12);
|
||||
Widget item = new Container(
|
||||
height: itemExtent,
|
||||
key: new ObjectKey(displayedMonth),
|
||||
child: new DayPicker(
|
||||
selectedDate: selectedDate,
|
||||
currentDate: _currentDate,
|
||||
onChanged: onChanged,
|
||||
displayedMonth: displayedMonth
|
||||
)
|
||||
);
|
||||
result.add(item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Scrollable list of years to allow picking a year
|
||||
class YearPicker extends ScrollableWidgetList {
|
||||
YearPicker({
|
||||
this.selectedDate,
|
||||
this.onChanged,
|
||||
this.firstDate,
|
||||
this.lastDate
|
||||
}) : super(itemExtent: 50.0) {
|
||||
assert(selectedDate != null);
|
||||
assert(lastDate.isAfter(firstDate));
|
||||
}
|
||||
DateTime selectedDate;
|
||||
DatePickerValueChanged onChanged;
|
||||
DateTime firstDate;
|
||||
DateTime lastDate;
|
||||
|
||||
void syncConstructorArguments(YearPicker source) {
|
||||
selectedDate = source.selectedDate;
|
||||
onChanged = source.onChanged;
|
||||
firstDate = source.firstDate;
|
||||
lastDate = source.lastDate;
|
||||
super.syncConstructorArguments(source);
|
||||
}
|
||||
|
||||
int get itemCount => lastDate.year - firstDate.year + 1;
|
||||
|
||||
List<Widget> buildItems(int start, int count) {
|
||||
TextStyle style = Theme.of(this).text.body1.copyWith(color: colors.black54);
|
||||
List<Widget> items = new List<Widget>();
|
||||
for(int i = start; i < start + count; i++) {
|
||||
int year = firstDate.year + i;
|
||||
String label = year.toString();
|
||||
Widget item = new Listener(
|
||||
key: new Key(label),
|
||||
onGestureTap: (_) {
|
||||
DateTime result = new DateTime(year, selectedDate.month, selectedDate.day);
|
||||
if (onChanged != null)
|
||||
onChanged(result);
|
||||
},
|
||||
child: new InkWell(
|
||||
child: new Container(
|
||||
height: itemExtent,
|
||||
decoration: year == selectedDate.year ? new BoxDecoration(
|
||||
backgroundColor: Theme.of(this).primarySwatch[100],
|
||||
shape: Shape.circle
|
||||
) : null,
|
||||
child: new Center(
|
||||
child: new Text(label, style: style)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
items.add(item);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,9 @@ class Dialog extends Component {
|
||||
Dialog({
|
||||
Key key,
|
||||
this.title,
|
||||
this.titlePadding,
|
||||
this.content,
|
||||
this.contentPadding,
|
||||
this.actions,
|
||||
this.onDismiss
|
||||
}): super(key: key);
|
||||
@@ -34,10 +36,17 @@ class Dialog extends Component {
|
||||
/// of the dialog.
|
||||
final Widget title;
|
||||
|
||||
// Padding around the title; uses material design default if none is supplied
|
||||
// If there is no title, no padding will be provided
|
||||
final EdgeDims titlePadding;
|
||||
|
||||
/// The (optional) content of the dialog is displayed in the center of the
|
||||
/// dialog in a lighter font.
|
||||
final Widget content;
|
||||
|
||||
// Padding around the content; uses material design default if none is supplied
|
||||
final EdgeDims contentPadding;
|
||||
|
||||
/// The (optional) set of actions that are displayed at the bottom of the
|
||||
/// dialog.
|
||||
final List<Widget> actions;
|
||||
@@ -59,8 +68,11 @@ class Dialog extends Component {
|
||||
List<Widget> dialogBody = new List<Widget>();
|
||||
|
||||
if (title != null) {
|
||||
EdgeDims padding = titlePadding;
|
||||
if (padding == null)
|
||||
padding = new EdgeDims(24.0, 24.0, content == null ? 20.0 : 0.0, 24.0);
|
||||
dialogBody.add(new Padding(
|
||||
padding: new EdgeDims(24.0, 24.0, content == null ? 20.0 : 0.0, 24.0),
|
||||
padding: padding,
|
||||
child: new DefaultTextStyle(
|
||||
style: Theme.of(this).text.title,
|
||||
child: title
|
||||
@@ -69,8 +81,11 @@ class Dialog extends Component {
|
||||
}
|
||||
|
||||
if (content != null) {
|
||||
EdgeDims padding = contentPadding;
|
||||
if (padding == null)
|
||||
padding = const EdgeDims(20.0, 24.0, 24.0, 24.0);
|
||||
dialogBody.add(new Padding(
|
||||
padding: const EdgeDims(20.0, 24.0, 24.0, 24.0),
|
||||
padding: padding,
|
||||
child: new DefaultTextStyle(
|
||||
style: Theme.of(this).text.subhead,
|
||||
child: content
|
||||
|
||||
@@ -13,5 +13,6 @@ dependencies:
|
||||
sky_services: ^0.0.14
|
||||
sky_tools: ^0.0.10
|
||||
vector_math: ^1.4.3
|
||||
intl: ^0.12.4+2
|
||||
environment:
|
||||
sdk: '>=1.8.0 <2.0.0'
|
||||
|
||||
Reference in New Issue
Block a user