| // 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 stocks; |
| |
| typedef void ModeUpdater(StockMode mode); |
| |
| class StockHome extends StatefulComponent { |
| StockHome(this.stocks, this.symbols, this.stockMode, this.modeUpdater); |
| |
| final Map<String, Stock> stocks; |
| final List<String> symbols; |
| final StockMode stockMode; |
| final ModeUpdater modeUpdater; |
| |
| StockHomeState createState() => new StockHomeState(); |
| } |
| |
| class StockHomeState extends State<StockHome> { |
| |
| final GlobalKey<PlaceholderState> _snackBarPlaceholderKey = new GlobalKey<PlaceholderState>(); |
| bool _isSearching = false; |
| String _searchQuery; |
| |
| void _handleSearchBegin() { |
| Navigator.of(context).push(new StateRoute( |
| onPop: () { |
| setState(() { |
| _isSearching = false; |
| _searchQuery = null; |
| }); |
| } |
| )); |
| setState(() { |
| _isSearching = true; |
| }); |
| } |
| |
| void _handleSearchEnd() { |
| Navigator.of(context).pop(); |
| } |
| |
| void _handleSearchQueryChanged(String query) { |
| setState(() { |
| _searchQuery = query; |
| }); |
| } |
| |
| bool _autorefresh = false; |
| void _handleAutorefreshChanged(bool value) { |
| setState(() { |
| _autorefresh = value; |
| }); |
| } |
| |
| void _handleStockModeChange(StockMode value) { |
| if (config.modeUpdater != null) |
| config.modeUpdater(value); |
| } |
| |
| void _handleMenuShow() { |
| showStockMenu( |
| context: context, |
| autorefresh: _autorefresh, |
| onAutorefreshChanged: _handleAutorefreshChanged |
| ); |
| } |
| |
| void _showDrawer() { |
| showDrawer( |
| context: context, |
| child: new Block(<Widget>[ |
| new DrawerHeader(child: new Text('Stocks')), |
| new DrawerItem( |
| icon: 'action/assessment', |
| selected: true, |
| child: new Text('Stock List') |
| ), |
| new DrawerItem( |
| icon: 'action/account_balance', |
| onPressed: () { |
| showDialog( |
| context: context, |
| child: new Dialog( |
| title: new Text('Not Implemented'), |
| content: new Text('This feature has not yet been implemented.'), |
| actions: <Widget>[ |
| new FlatButton( |
| child: new Text('USE IT'), |
| onPressed: () { |
| Navigator.of(context).pop(false); |
| } |
| ), |
| new FlatButton( |
| child: new Text('OH WELL'), |
| onPressed: () { |
| Navigator.of(context).pop(false); |
| } |
| ), |
| ] |
| ) |
| ); |
| }, |
| child: new Text('Account Balance') |
| ), |
| new DrawerItem( |
| icon: 'device/dvr', |
| onPressed: () { debugDumpApp(); debugDumpRenderTree(); }, |
| child: new Text('Dump App to Console') |
| ), |
| new DrawerDivider(), |
| new DrawerItem( |
| icon: 'action/thumb_up', |
| onPressed: () => _handleStockModeChange(StockMode.optimistic), |
| child: new Row(<Widget>[ |
| new Flexible(child: new Text('Optimistic')), |
| new Radio<StockMode>(value: StockMode.optimistic, groupValue: config.stockMode, onChanged: _handleStockModeChange) |
| ]) |
| ), |
| new DrawerItem( |
| icon: 'action/thumb_down', |
| onPressed: () => _handleStockModeChange(StockMode.pessimistic), |
| child: new Row(<Widget>[ |
| new Flexible(child: new Text('Pessimistic')), |
| new Radio<StockMode>(value: StockMode.pessimistic, groupValue: config.stockMode, onChanged: _handleStockModeChange) |
| ]) |
| ), |
| 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.of(context)..pop() |
| ..pushNamed('/settings'); |
| } |
| |
| Widget buildToolBar() { |
| return new ToolBar( |
| level: 0, |
| left: new IconButton( |
| icon: "navigation/menu", |
| onPressed: _showDrawer |
| ), |
| center: new Text('Stocks'), |
| right: <Widget>[ |
| new IconButton( |
| icon: "action/search", |
| onPressed: _handleSearchBegin |
| ), |
| new IconButton( |
| icon: "navigation/more_vert", |
| onPressed: _handleMenuShow |
| ) |
| ] |
| ); |
| } |
| |
| int selectedTabIndex = 0; |
| |
| Iterable<Stock> _getStockList(Iterable<String> symbols) { |
| return symbols.map((String symbol) => config.stocks[symbol]) |
| .where((Stock stock) => stock != null); |
| } |
| |
| Iterable<Stock> _filterBySearchQuery(Iterable<Stock> stocks) { |
| if (_searchQuery == null) |
| return stocks; |
| RegExp regexp = new RegExp(_searchQuery, caseSensitive: false); |
| return stocks.where((Stock stock) => stock.symbol.contains(regexp)); |
| } |
| |
| Widget buildStockList(BuildContext context, Iterable<Stock> stocks) { |
| return new StockList( |
| stocks: stocks.toList(), |
| onAction: (Stock stock, Key arrowKey) { |
| setState(() { |
| stock.percentChange = 100.0 * (1.0 / stock.lastSale); |
| stock.lastSale += 1.0; |
| }); |
| }, |
| onOpen: (Stock stock, Key arrowKey) { |
| Set<Key> mostValuableKeys = new Set<Key>(); |
| mostValuableKeys.add(arrowKey); |
| Navigator.of(context).pushNamed('/stock/${stock.symbol}', mostValuableKeys: mostValuableKeys); |
| } |
| ); |
| } |
| |
| static const List<String> portfolioSymbols = const <String>["AAPL","FIZZ", "FIVE", "FLAT", "ZINC", "ZNGA"]; |
| |
| Widget buildTabNavigator() { |
| return new TabNavigator( |
| views: <TabNavigatorView>[ |
| new TabNavigatorView( |
| label: const TabLabel(text: 'MARKET'), |
| builder: (BuildContext context) => buildStockList(context, _filterBySearchQuery(_getStockList(config.symbols)).toList()) |
| ), |
| new TabNavigatorView( |
| label: const TabLabel(text: 'PORTFOLIO'), |
| builder: (BuildContext context) => buildStockList(context, _filterBySearchQuery(_getStockList(portfolioSymbols)).toList()) |
| ) |
| ], |
| selectedIndex: selectedTabIndex, |
| onChanged: (int tabIndex) { |
| setState(() { selectedTabIndex = tabIndex; } ); |
| } |
| ); |
| } |
| |
| static GlobalKey searchFieldKey = new GlobalKey(); |
| |
| // TODO(abarth): Should we factor this into a SearchBar in the framework? |
| Widget buildSearchBar() { |
| return new ToolBar( |
| left: new IconButton( |
| icon: "navigation/arrow_back", |
| colorFilter: new ColorFilter.mode(Theme.of(context).accentColor, ui.TransferMode.srcATop), |
| onPressed: _handleSearchEnd |
| ), |
| center: new Input( |
| key: searchFieldKey, |
| placeholder: 'Search stocks', |
| onChanged: _handleSearchQueryChanged |
| ), |
| backgroundColor: Theme.of(context).canvasColor |
| ); |
| } |
| |
| void _handleUndo() { |
| Navigator.of(context).pop(); |
| } |
| |
| void _handleStockPurchased() { |
| showSnackBar( |
| context: context, |
| placeholderKey: _snackBarPlaceholderKey, |
| content: new Text("Stock purchased!"), |
| actions: <SnackBarAction>[ |
| new SnackBarAction(label: "UNDO", onPressed: _handleUndo) |
| ] |
| ); |
| } |
| |
| Widget buildFloatingActionButton() { |
| return new FloatingActionButton( |
| child: new Icon(icon: 'content/add'), |
| backgroundColor: Colors.redAccent[200], |
| onPressed: _handleStockPurchased |
| ); |
| } |
| |
| Widget build(BuildContext context) { |
| return new Scaffold( |
| toolBar: _isSearching ? buildSearchBar() : buildToolBar(), |
| body: buildTabNavigator(), |
| snackBar: new Placeholder(key: _snackBarPlaceholderKey), |
| floatingActionButton: buildFloatingActionButton() |
| ); |
| } |
| } |