// 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. part of fitness; class FitnessItemList extends StatelessComponent { FitnessItemList({ Key key, this.items, this.onDismissed }) : super(key: key) { assert(items != null); assert(onDismissed != null); } final List<FitnessItem> items; final FitnessItemHandler onDismissed; Widget build(BuildContext context) { return new ScrollableList( padding: const EdgeDims.all(4.0), itemExtent: kFitnessItemHeight, children: items.map((FitnessItem item) => item.toRow(onDismissed: onDismissed)) ); } } class DialogMenuItem extends StatelessComponent { DialogMenuItem(this.children, { Key key, this.onPressed }) : super(key: key); List<Widget> children; Function onPressed; Widget build(BuildContext context) { return new Container( height: 48.0, child: new InkWell( onTap: onPressed, child: new Padding( padding: const EdgeDims.symmetric(horizontal: 16.0), child: new Row(children: children) ) ) ); } } class FeedFragment extends StatefulComponent { FeedFragment({ this.userData, this.onItemCreated, this.onItemDeleted }); final UserData userData; final FitnessItemHandler onItemCreated; final FitnessItemHandler onItemDeleted; FeedFragmentState createState() => new FeedFragmentState(); } class FeedFragmentState extends State<FeedFragment> { FitnessMode _fitnessMode = FitnessMode.feed; void _handleFitnessModeChange(FitnessMode value) { setState(() { _fitnessMode = value; }); Navigator.pop(context); } Widget _buildDrawer() { return new Drawer( child: new Block(children: <Widget>[ new DrawerHeader(child: new Text('Fitness')), new DrawerItem( icon: 'action/view_list', onPressed: () => _handleFitnessModeChange(FitnessMode.feed), selected: _fitnessMode == FitnessMode.feed, child: new Text('Feed')), new DrawerItem( icon: 'action/assessment', onPressed: () => _handleFitnessModeChange(FitnessMode.chart), selected: _fitnessMode == FitnessMode.chart, child: new Text('Chart')), new DrawerDivider(), new DrawerItem( icon: 'action/settings', onPressed: _handleShowSettings, child: new Text('Settings')), new DrawerItem( icon: 'action/help', child: new Text('Help & Feedback')) ]) ); } void _handleShowSettings() { Navigator.popAndPushNamed(context, '/settings'); } // TODO(jackson): We should be localizing String get fitnessModeTitle { switch(_fitnessMode) { case FitnessMode.feed: return "Feed"; case FitnessMode.chart: return "Chart"; } } Widget buildToolBar() { return new ToolBar( center: new Text(fitnessModeTitle) ); } void _handleItemDismissed(FitnessItem item) { config.onItemDeleted(item); Scaffold.of(context).showSnackBar(new SnackBar( content: new Text("Item deleted."), actions: <SnackBarAction>[ new SnackBarAction(label: "UNDO", onPressed: () { config.onItemCreated(item); }), ] )); } Widget buildChart() { double startX; double endX; double startY; double endY; List<Point> dataSet = new List<Point>(); for (FitnessItem item in config.userData.items) { if (item is Measurement) { double x = item.when.millisecondsSinceEpoch.toDouble(); double y = item.weight; if (startX == null || startX > x) startX = x; if (endX == null || endX < x) endX = x; if (startY == null || startY > y) startY = y; if (endY == null || endY < y) endY = y; dataSet.add(new Point(x, y)); } } if (config.userData.goalWeight != null && config.userData.goalWeight > 0.0) { startY = math.min(startY, config.userData.goalWeight); endY = math.max(endY, config.userData.goalWeight); } playfair.ChartData data = new playfair.ChartData( startX: startX, startY: startY, endX: endX, endY: endY, dataSet: dataSet, numHorizontalGridlines: 5, roundToPlaces: 1, indicatorLine: config.userData.goalWeight, indicatorText: "GOAL WEIGHT" ); return new playfair.Chart(data: data); } Widget buildBody() { TextStyle style = Theme.of(context).text.title; if (config.userData == null) return new Container(); if (config.userData.items.length == 0) { return new Row( children: <Widget>[new Text("No data yet.\nAdd some!", style: style)], justifyContent: FlexJustifyContent.center ); } switch (_fitnessMode) { case FitnessMode.feed: return new FitnessItemList( items: config.userData.items.reversed.toList(), onDismissed: _handleItemDismissed ); case FitnessMode.chart: return new Container( padding: const EdgeDims.all(20.0), child: buildChart() ); } } void _handleActionButtonPressed() { showDialog(context: context, child: new AddItemDialog()).then((routeName) { if (routeName != null) Navigator.pushNamed(context, routeName); }); } Widget buildFloatingActionButton() { switch (_fitnessMode) { case FitnessMode.feed: return new FloatingActionButton( child: new Icon(icon: 'content/add'), onPressed: _handleActionButtonPressed ); case FitnessMode.chart: return null; } } Widget build(BuildContext context) { return new Scaffold( toolBar: buildToolBar(), body: buildBody(), floatingActionButton: buildFloatingActionButton(), drawer: _buildDrawer() ); } } class AddItemDialog extends StatefulComponent { AddItemDialogState createState() => new AddItemDialogState(); } class AddItemDialogState extends State<AddItemDialog> { // TODO(jackson): Internationalize static final Map<String, String> _labels = <String, String>{ '/measurements/new': 'Measure', '/meals/new': 'Eat', }; String _addItemRoute = _labels.keys.first; void _handleAddItemRouteChanged(String routeName) { setState(() { _addItemRoute = routeName; }); } Widget build(BuildContext context) { List<Widget> menuItems = <Widget>[]; for (String routeName in _labels.keys) { menuItems.add(new DialogMenuItem(<Widget>[ new Flexible(child: new Text(_labels[routeName])), new Radio<String>(value: routeName, groupValue: _addItemRoute, onChanged: _handleAddItemRouteChanged), ], onPressed: () => _handleAddItemRouteChanged(routeName))); } return new Dialog( title: new Text("What are you doing?"), content: new Block(children: menuItems), actions: <Widget>[ new FlatButton( child: new Text('CANCEL'), onPressed: () { Navigator.pop(context); } ), new FlatButton( child: new Text('ADD'), onPressed: () { Navigator.pop(context, _addItemRoute); } ), ] ); } }