
We were trying to cram too much functionality in to the Dialog widget. Now we have AlertDialog and SimpleDialog to cover to two different kinds of dialogs in the spec.
368 lines
11 KiB
Dart
368 lines
11 KiB
Dart
// 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:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart' show debugDumpRenderTree, debugDumpLayerTree, debugDumpSemanticsTree;
|
|
import 'package:flutter/scheduler.dart' show timeDilation;
|
|
import 'stock_data.dart';
|
|
import 'stock_list.dart';
|
|
import 'stock_strings.dart';
|
|
import 'stock_symbol_viewer.dart';
|
|
import 'stock_types.dart';
|
|
|
|
typedef void ModeUpdater(StockMode mode);
|
|
|
|
enum _StockMenuItem { autorefresh, refresh, speedUp, speedDown }
|
|
enum StockHomeTab { market, portfolio }
|
|
|
|
class _NotImplementedDialog extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new AlertDialog(
|
|
title: new Text('Not Implemented'),
|
|
content: new Text('This feature has not yet been implemented.'),
|
|
actions: <Widget>[
|
|
new FlatButton(
|
|
onPressed: () { debugDumpApp(); },
|
|
child: new Row(
|
|
children: <Widget>[
|
|
new Icon(
|
|
Icons.dvr,
|
|
size: 18.0
|
|
),
|
|
new Container(
|
|
width: 8.0
|
|
),
|
|
new Text('DUMP APP TO CONSOLE'),
|
|
]
|
|
)
|
|
),
|
|
new FlatButton(
|
|
onPressed: () {
|
|
Navigator.pop(context, false);
|
|
},
|
|
child: new Text('OH WELL')
|
|
)
|
|
]
|
|
);
|
|
}
|
|
}
|
|
|
|
class StockHome extends StatefulWidget {
|
|
const StockHome(this.stocks, this.symbols, this.configuration, this.updater);
|
|
|
|
final Map<String, Stock> stocks;
|
|
final List<String> symbols;
|
|
final StockConfiguration configuration;
|
|
final ValueChanged<StockConfiguration> updater;
|
|
|
|
@override
|
|
StockHomeState createState() => new StockHomeState();
|
|
}
|
|
|
|
class StockHomeState extends State<StockHome> {
|
|
|
|
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
|
bool _isSearching = false;
|
|
InputValue _searchQuery = InputValue.empty;
|
|
bool _autorefresh = false;
|
|
|
|
void _handleSearchBegin() {
|
|
ModalRoute.of(context).addLocalHistoryEntry(new LocalHistoryEntry(
|
|
onRemove: () {
|
|
setState(() {
|
|
_isSearching = false;
|
|
_searchQuery = InputValue.empty;
|
|
});
|
|
}
|
|
));
|
|
setState(() {
|
|
_isSearching = true;
|
|
});
|
|
}
|
|
|
|
void _handleSearchEnd() {
|
|
Navigator.pop(context);
|
|
}
|
|
|
|
void _handleSearchQueryChanged(InputValue query) {
|
|
setState(() {
|
|
_searchQuery = query;
|
|
});
|
|
}
|
|
|
|
void _handleStockModeChange(StockMode value) {
|
|
if (config.updater != null)
|
|
config.updater(config.configuration.copyWith(stockMode: value));
|
|
}
|
|
|
|
void _handleStockMenu(BuildContext context, _StockMenuItem value) {
|
|
switch(value) {
|
|
case _StockMenuItem.autorefresh:
|
|
setState(() {
|
|
_autorefresh = !_autorefresh;
|
|
});
|
|
break;
|
|
case _StockMenuItem.refresh:
|
|
showDialog(
|
|
context: context,
|
|
child: new _NotImplementedDialog()
|
|
);
|
|
break;
|
|
case _StockMenuItem.speedUp:
|
|
timeDilation /= 5.0;
|
|
break;
|
|
case _StockMenuItem.speedDown:
|
|
timeDilation *= 5.0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Widget _buildDrawer(BuildContext context) {
|
|
return new Drawer(
|
|
child: new Block(children: <Widget>[
|
|
new DrawerHeader(child: new Center(child: new Text('Stocks'))),
|
|
new DrawerItem(
|
|
icon: new Icon(Icons.assessment),
|
|
selected: true,
|
|
child: new Text('Stock List')
|
|
),
|
|
new DrawerItem(
|
|
icon: new Icon(Icons.account_balance),
|
|
onPressed: null,
|
|
child: new Text('Account Balance')
|
|
),
|
|
new DrawerItem(
|
|
icon: new Icon(Icons.dvr),
|
|
onPressed: () {
|
|
try {
|
|
debugDumpApp();
|
|
debugDumpRenderTree();
|
|
debugDumpLayerTree();
|
|
debugDumpSemanticsTree();
|
|
} catch (e, stack) {
|
|
debugPrint('Exception while dumping app:\n$e\n$stack');
|
|
}
|
|
},
|
|
child: new Text('Dump App to Console')
|
|
),
|
|
new Divider(),
|
|
new DrawerItem(
|
|
icon: new Icon(Icons.thumb_up),
|
|
onPressed: () => _handleStockModeChange(StockMode.optimistic),
|
|
child: new Row(
|
|
children: <Widget>[
|
|
new Flexible(child: new Text('Optimistic')),
|
|
new Radio<StockMode>(value: StockMode.optimistic, groupValue: config.configuration.stockMode, onChanged: _handleStockModeChange)
|
|
]
|
|
)
|
|
),
|
|
new DrawerItem(
|
|
icon: new Icon(Icons.thumb_down),
|
|
onPressed: () => _handleStockModeChange(StockMode.pessimistic),
|
|
child: new Row(
|
|
children: <Widget>[
|
|
new Flexible(child: new Text('Pessimistic')),
|
|
new Radio<StockMode>(value: StockMode.pessimistic, groupValue: config.configuration.stockMode, onChanged: _handleStockModeChange)
|
|
]
|
|
)
|
|
),
|
|
new Divider(),
|
|
new DrawerItem(
|
|
icon: new Icon(Icons.settings),
|
|
onPressed: _handleShowSettings,
|
|
child: new Text('Settings')),
|
|
new DrawerItem(
|
|
icon: new Icon(Icons.help),
|
|
onPressed: _handleShowAbout,
|
|
child: new Text('About'))
|
|
])
|
|
);
|
|
}
|
|
|
|
void _handleShowSettings() {
|
|
Navigator.popAndPushNamed(context, '/settings');
|
|
}
|
|
|
|
void _handleShowAbout() {
|
|
showAboutDialog(context: context);
|
|
}
|
|
|
|
Widget buildAppBar() {
|
|
return new AppBar(
|
|
elevation: 0,
|
|
title: new Text(StockStrings.of(context).title()),
|
|
actions: <Widget>[
|
|
new IconButton(
|
|
icon: new Icon(Icons.search),
|
|
onPressed: _handleSearchBegin,
|
|
tooltip: 'Search'
|
|
),
|
|
new PopupMenuButton<_StockMenuItem>(
|
|
onSelected: (_StockMenuItem value) { _handleStockMenu(context, value); },
|
|
itemBuilder: (BuildContext context) => <PopupMenuItem<_StockMenuItem>>[
|
|
new CheckedPopupMenuItem<_StockMenuItem>(
|
|
value: _StockMenuItem.autorefresh,
|
|
checked: _autorefresh,
|
|
child: new Text('Autorefresh')
|
|
),
|
|
new PopupMenuItem<_StockMenuItem>(
|
|
value: _StockMenuItem.refresh,
|
|
child: new Text('Refresh')
|
|
),
|
|
new PopupMenuItem<_StockMenuItem>(
|
|
value: _StockMenuItem.speedUp,
|
|
child: new Text('Increase animation speed')
|
|
),
|
|
new PopupMenuItem<_StockMenuItem>(
|
|
value: _StockMenuItem.speedDown,
|
|
child: new Text('Decrease animation speed')
|
|
)
|
|
]
|
|
)
|
|
],
|
|
bottom: new TabBar<StockHomeTab>(
|
|
labels: <StockHomeTab, TabLabel>{
|
|
StockHomeTab.market: new TabLabel(text: StockStrings.of(context).market()),
|
|
StockHomeTab.portfolio: new TabLabel(text: StockStrings.of(context).portfolio())
|
|
}
|
|
)
|
|
);
|
|
}
|
|
|
|
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.text.isEmpty)
|
|
return stocks;
|
|
RegExp regexp = new RegExp(_searchQuery.text, caseSensitive: false);
|
|
return stocks.where((Stock stock) => stock.symbol.contains(regexp));
|
|
}
|
|
|
|
void _buyStock(Stock stock) {
|
|
setState(() {
|
|
stock.percentChange = 100.0 * (1.0 / stock.lastSale);
|
|
stock.lastSale += 1.0;
|
|
});
|
|
_scaffoldKey.currentState.showSnackBar(new SnackBar(
|
|
content: new Text("Purchased ${stock.symbol} for ${stock.lastSale}"),
|
|
action: new SnackBarAction(
|
|
label: "BUY MORE",
|
|
onPressed: () {
|
|
_buyStock(stock);
|
|
}
|
|
)
|
|
));
|
|
}
|
|
|
|
Widget _buildStockList(BuildContext context, Iterable<Stock> stocks, StockHomeTab tab) {
|
|
return new StockList(
|
|
stocks: stocks.toList(),
|
|
onAction: _buyStock,
|
|
onOpen: (Stock stock) {
|
|
Navigator.pushNamed(context, '/stock/${stock.symbol}');
|
|
},
|
|
onShow: (Stock stock) {
|
|
_scaffoldKey.currentState.showBottomSheet((BuildContext context) => new StockSymbolBottomSheet(stock: stock));
|
|
}
|
|
);
|
|
}
|
|
|
|
Widget _buildStockTab(BuildContext context, StockHomeTab tab, List<String> stockSymbols) {
|
|
return new Container(
|
|
key: new ValueKey<StockHomeTab>(tab),
|
|
child: _buildStockList(context, _filterBySearchQuery(_getStockList(stockSymbols)).toList(), tab)
|
|
);
|
|
}
|
|
|
|
static const List<String> portfolioSymbols = const <String>["AAPL","FIZZ", "FIVE", "FLAT", "ZINC", "ZNGA"];
|
|
|
|
// TODO(abarth): Should we factor this into a SearchBar in the framework?
|
|
Widget buildSearchBar() {
|
|
return new AppBar(
|
|
leading: new IconButton(
|
|
icon: new Icon(Icons.arrow_back),
|
|
color: Theme.of(context).accentColor,
|
|
onPressed: _handleSearchEnd,
|
|
tooltip: 'Back'
|
|
),
|
|
title: new Input(
|
|
value: _searchQuery,
|
|
autofocus: true,
|
|
hintText: 'Search stocks',
|
|
onChanged: _handleSearchQueryChanged
|
|
),
|
|
backgroundColor: Theme.of(context).canvasColor
|
|
);
|
|
}
|
|
|
|
void _handleCreateCompany() {
|
|
showModalBottomSheet/*<Null>*/(
|
|
context: context,
|
|
builder: (BuildContext context) => new _CreateCompanySheet()
|
|
);
|
|
}
|
|
|
|
Widget buildFloatingActionButton() {
|
|
return new FloatingActionButton(
|
|
tooltip: 'Create company',
|
|
child: new Icon(Icons.add),
|
|
backgroundColor: Colors.redAccent[200],
|
|
onPressed: _handleCreateCompany
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new TabBarSelection<StockHomeTab>(
|
|
values: <StockHomeTab>[StockHomeTab.market, StockHomeTab.portfolio],
|
|
child: new Scaffold(
|
|
key: _scaffoldKey,
|
|
appBar: _isSearching ? buildSearchBar() : buildAppBar(),
|
|
floatingActionButton: buildFloatingActionButton(),
|
|
drawer: _buildDrawer(context),
|
|
body: new TabBarView<StockHomeTab>(
|
|
children: <Widget>[
|
|
_buildStockTab(context, StockHomeTab.market, config.symbols),
|
|
_buildStockTab(context, StockHomeTab.portfolio, portfolioSymbols),
|
|
]
|
|
)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
class _CreateCompanySheet extends StatefulWidget {
|
|
@override
|
|
_CreateCompanySheetState createState() => new _CreateCompanySheetState();
|
|
}
|
|
|
|
class _CreateCompanySheetState extends State<_CreateCompanySheet> {
|
|
InputValue _companyName = InputValue.empty;
|
|
|
|
void _handleCompanyNameChanged(InputValue value) {
|
|
setState(() {
|
|
_companyName = value;
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
// TODO(ianh): Fill this out.
|
|
return new Column(
|
|
children: <Widget>[
|
|
new Input(
|
|
autofocus: true,
|
|
hintText: 'Company Name',
|
|
value: _companyName,
|
|
onChanged: _handleCompanyNameChanged
|
|
),
|
|
]
|
|
);
|
|
}
|
|
}
|