Commit a4b7cb37 authored by Ian Hickson's avatar Ian Hickson

Merge pull request #674 from Hixie/duplicate-keys-in-stocks

Fix crash when going back in stocks app.
parents 38aa83b0 d5e072c3
......@@ -199,8 +199,9 @@ class StockHomeState extends State<StockHome> {
));
}
Widget buildStockList(BuildContext context, Iterable<Stock> stocks) {
Widget _buildStockList(BuildContext context, Iterable<Stock> stocks, StockHomeTab tab) {
return new StockList(
keySalt: tab,
stocks: stocks.toList(),
onAction: _buyStock,
onOpen: (Stock stock, Key arrowKey) {
......@@ -214,6 +215,13 @@ class StockHomeState extends State<StockHome> {
);
}
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"];
static GlobalKey searchFieldKey = new GlobalKey();
......@@ -259,13 +267,6 @@ class StockHomeState extends State<StockHome> {
);
}
Widget buildStockTab(BuildContext context, StockHomeTab tab, List<String> stockSymbols) {
return new Container(
key: new ValueKey<StockHomeTab>(tab),
child: buildStockList(context, _filterBySearchQuery(_getStockList(stockSymbols)).toList())
);
}
double _viewWidth = 100.0;
void _handleSizeChanged(Size newSize) {
setState(() {
......@@ -282,14 +283,14 @@ class StockHomeState extends State<StockHome> {
onSizeChanged: _handleSizeChanged,
child: new TabBarView<StockHomeTab>(
selection: _tabBarSelection,
items: [StockHomeTab.market, StockHomeTab.portfolio],
items: <StockHomeTab>[StockHomeTab.market, StockHomeTab.portfolio],
itemExtent: _viewWidth,
itemBuilder: (BuildContext context, StockHomeTab tab, _) {
switch (tab) {
case StockHomeTab.market:
return buildStockTab(context, tab, config.symbols);
return _buildStockTab(context, tab, config.symbols);
case StockHomeTab.portfolio:
return buildStockTab(context, tab, portfolioSymbols);
return _buildStockTab(context, tab, portfolioSymbols);
default:
assert(false);
}
......
......@@ -5,8 +5,9 @@
part of stocks;
class StockList extends StatelessComponent {
StockList({ Key key, this.stocks, this.onOpen, this.onShow, this.onAction }) : super(key: key);
StockList({ Key key, this.keySalt, this.stocks, this.onOpen, this.onShow, this.onAction }) : super(key: key);
final Object keySalt;
final List<Stock> stocks;
final StockRowActionCallback onOpen;
final StockRowActionCallback onShow;
......@@ -18,6 +19,7 @@ class StockList extends StatelessComponent {
itemExtent: StockRow.kHeight,
itemBuilder: (BuildContext context, Stock stock, int index) {
return new StockRow(
keySalt: keySalt,
stock: stock,
onPressed: onOpen,
onDoubleTap: onShow,
......
......@@ -7,18 +7,22 @@ part of stocks;
enum StockRowPartKind { arrow }
class StockRowPartKey extends Key {
const StockRowPartKey(this.stock, this.part) : super.constructor();
const StockRowPartKey(this.keySalt, this.stock, this.part) : super.constructor();
final Object keySalt;
final Stock stock;
final StockRowPartKind part;
bool operator ==(dynamic other) {
if (other is! StockRowPartKey)
if (identical(this, other))
return true;
if (other.runtimeType != runtimeType)
return false;
final StockRowPartKey typedOther = other;
return stock == typedOther.stock &&
part == typedOther.part;
return keySalt == typedOther.keySalt
&& stock == typedOther.stock
&& part == typedOther.part;
}
int get hashCode => 37 * (37 * (373) + identityHashCode(stock)) + identityHashCode(part);
String toString() => '[StockRowPartKey ${stock.symbol}:${part.toString().split(".")[1]})]';
int get hashCode => 37 * (37 * (37 * (373) + identityHashCode(keySalt)) + identityHashCode(stock)) + identityHashCode(part);
String toString() => '[$runtimeType ${keySalt.toString().split(".")[1]}:${stock.symbol}:${part.toString().split(".")[1]}]';
}
typedef void StockRowActionCallback(Stock stock, Key arrowKey);
......@@ -26,11 +30,12 @@ typedef void StockRowActionCallback(Stock stock, Key arrowKey);
class StockRow extends StatelessComponent {
StockRow({
Stock stock,
Object keySalt,
this.onPressed,
this.onDoubleTap,
this.onLongPressed
}) : this.stock = stock,
_arrowKey = new StockRowPartKey(stock, StockRowPartKind.arrow),
_arrowKey = new StockRowPartKey(keySalt, stock, StockRowPartKind.arrow),
super(key: new ObjectKey(stock));
final Stock stock;
......
......@@ -88,6 +88,8 @@ class HeroController extends NavigatorObserver {
}
Set<Key> _getMostValuableKeys() {
assert(_from != null);
assert(_to != null);
Set<Key> result = new Set<Key>();
if (_from.settings.mostValuableKeys != null)
result.addAll(_from.settings.mostValuableKeys);
......
......@@ -107,7 +107,19 @@ class Hero extends StatefulComponent {
assert(tag != null);
Key key = hero.widget.key;
final Map<Key, HeroState> tagHeroes = heroes.putIfAbsent(tag, () => <Key, HeroState>{});
assert(!tagHeroes.containsKey(key));
assert(() {
if (tagHeroes.containsKey(key)) {
debugPrint('Tag: $tag Key: $key');
assert(() {
'There are multiple heroes that share the same key within the same subtree. '
'Within each subtree for which heroes are to be animated (typically a PageRoute subtree), '
'either each Hero must have a unique tag, or, all the heroes with a particular tag must '
'have different keys. The relevant tag and key were dumped above. ';
return false;
});
}
return true;
});
tagHeroes[key] = hero.state;
}
element.visitChildren(visitor);
......
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