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 { ...@@ -13,6 +13,12 @@ class DateUtils {
static const MS_IN_WEEK = static const MS_IN_WEEK =
DateTime.DAYS_PER_WEEK * Duration.MILLISECONDS_PER_DAY; 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 */ /** Formats a time in H:MM A format */
static String toHourMinutesString(Duration duration) { static String toHourMinutesString(Duration duration) {
assert(duration.inDays == 0); assert(duration.inDays == 0);
...@@ -68,14 +74,16 @@ class DateUtils { ...@@ -68,14 +74,16 @@ class DateUtils {
} else if (delta.inMilliseconds < MS_IN_WEEK) { } else if (delta.inMilliseconds < MS_IN_WEEK) {
return WEEKDAYS[then.weekday]; return WEEKDAYS[then.weekday];
} else { } else {
// TODO(jmesserly): locale specific date format String twoDigitMonth = _twoDigits(then.month);
String twoDigits(int n) { String twoDigitDay = _twoDigits(then.day);
if (n >= 10) return "${n}";
return "0${n}";
}
String twoDigitMonth = twoDigits(then.month);
String twoDigitDay = twoDigits(then.day);
return "${then.year}-${twoDigitMonth}-${twoDigitDay}"; 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 { ...@@ -21,7 +21,7 @@ abstract class FitnessItem {
Map toJson() => { 'when' : when.toIso8601String() }; Map toJson() => { 'when' : when.toIso8601String() };
// TODO(jackson): Internationalize // TODO(jackson): Internationalize
String get displayDate => DateUtils.toRecentTimeString(when); String get displayDate => DateUtils.toDateString(when);
FitnessItemRow toRow({ FitnessItemHandler onDismissed }); FitnessItemRow toRow({ FitnessItemHandler onDismissed });
} }
......
...@@ -17,6 +17,7 @@ class Measurement extends FitnessItem { ...@@ -17,6 +17,7 @@ class Measurement extends FitnessItem {
Map toJson() { Map toJson() {
Map json = super.toJson(); Map json = super.toJson();
json['weight'] = weight; json['weight'] = weight;
json['type'] = runtimeType.toString();
return json; return json;
} }
...@@ -53,6 +54,55 @@ class MeasurementRow extends FitnessItemRow { ...@@ -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 { class MeasurementFragment extends StatefulComponent {
MeasurementFragment({ this.navigator, this.onCreated }); MeasurementFragment({ this.navigator, this.onCreated });
...@@ -66,6 +116,7 @@ class MeasurementFragment extends StatefulComponent { ...@@ -66,6 +116,7 @@ class MeasurementFragment extends StatefulComponent {
} }
String _weight = ""; String _weight = "";
DateTime _when = new DateTime.now();
String _errorMessage = null; String _errorMessage = null;
EventDisposition _handleSave() { EventDisposition _handleSave() {
...@@ -79,7 +130,7 @@ class MeasurementFragment extends StatefulComponent { ...@@ -79,7 +130,7 @@ class MeasurementFragment extends StatefulComponent {
}); });
return EventDisposition.processed; return EventDisposition.processed;
} }
onCreated(new Measurement(when: new DateTime.now(), weight: parsedWeight)); onCreated(new Measurement(when: _when, weight: parsedWeight));
navigator.pop(); navigator.pop();
return EventDisposition.processed; return EventDisposition.processed;
} }
...@@ -107,23 +158,44 @@ class MeasurementFragment extends StatefulComponent { ...@@ -107,23 +158,44 @@ class MeasurementFragment extends StatefulComponent {
static final GlobalKey weightKey = new GlobalKey(); 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() { 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( return new Material(
type: MaterialType.canvas, type: MaterialType.canvas,
child: new ScrollableViewport( child: new Container(
child: new Container( padding: const EdgeDims.all(20.0),
padding: const EdgeDims.all(20.0), child: new Column([
child: new BlockBody([ new Listener(
new Text(measurement.displayDate), onGestureTap: _handleDatePressed,
new Input( child: new Container(
key: weightKey, height: 50.0,
placeholder: 'Enter weight', child: new Column([
keyboardType: KeyboardType_NUMBER, new Text('Measurement Date'),
onChanged: _handleWeightChanged 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'; ...@@ -10,6 +10,7 @@ export 'widgets/basic.dart';
export 'widgets/button_base.dart'; export 'widgets/button_base.dart';
export 'widgets/card.dart'; export 'widgets/card.dart';
export 'widgets/checkbox.dart'; export 'widgets/checkbox.dart';
export 'widgets/date_picker.dart';
export 'widgets/default_text_style.dart'; export 'widgets/default_text_style.dart';
export 'widgets/dialog.dart'; export 'widgets/dialog.dart';
export 'widgets/dismissable.dart'; export 'widgets/dismissable.dart';
......
This diff is collapsed.
...@@ -25,7 +25,9 @@ class Dialog extends Component { ...@@ -25,7 +25,9 @@ class Dialog extends Component {
Dialog({ Dialog({
Key key, Key key,
this.title, this.title,
this.titlePadding,
this.content, this.content,
this.contentPadding,
this.actions, this.actions,
this.onDismiss this.onDismiss
}): super(key: key); }): super(key: key);
...@@ -34,10 +36,17 @@ class Dialog extends Component { ...@@ -34,10 +36,17 @@ class Dialog extends Component {
/// of the dialog. /// of the dialog.
final Widget title; 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 /// The (optional) content of the dialog is displayed in the center of the
/// dialog in a lighter font. /// dialog in a lighter font.
final Widget content; 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 /// The (optional) set of actions that are displayed at the bottom of the
/// dialog. /// dialog.
final List<Widget> actions; final List<Widget> actions;
...@@ -59,8 +68,11 @@ class Dialog extends Component { ...@@ -59,8 +68,11 @@ class Dialog extends Component {
List<Widget> dialogBody = new List<Widget>(); List<Widget> dialogBody = new List<Widget>();
if (title != null) { 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( dialogBody.add(new Padding(
padding: new EdgeDims(24.0, 24.0, content == null ? 20.0 : 0.0, 24.0), padding: padding,
child: new DefaultTextStyle( child: new DefaultTextStyle(
style: Theme.of(this).text.title, style: Theme.of(this).text.title,
child: title child: title
...@@ -69,8 +81,11 @@ class Dialog extends Component { ...@@ -69,8 +81,11 @@ class Dialog extends Component {
} }
if (content != null) { if (content != null) {
EdgeDims padding = contentPadding;
if (padding == null)
padding = const EdgeDims(20.0, 24.0, 24.0, 24.0);
dialogBody.add(new Padding( dialogBody.add(new Padding(
padding: const EdgeDims(20.0, 24.0, 24.0, 24.0), padding: padding,
child: new DefaultTextStyle( child: new DefaultTextStyle(
style: Theme.of(this).text.subhead, style: Theme.of(this).text.subhead,
child: content child: content
......
...@@ -13,5 +13,6 @@ dependencies: ...@@ -13,5 +13,6 @@ dependencies:
sky_services: ^0.0.14 sky_services: ^0.0.14
sky_tools: ^0.0.10 sky_tools: ^0.0.10
vector_math: ^1.4.3 vector_math: ^1.4.3
intl: ^0.12.4+2
environment: environment:
sdk: '>=1.8.0 <2.0.0' 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