Commit f164e560 authored by Collin Jackson's avatar Collin Jackson

Add date picker to widgets library and teach fitness app to use it

Also, add an example for the date picker
parent ee5f1146
......@@ -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)
)
);
}
......
// 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';
......
This diff is collapsed.
......@@ -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'
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