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> { ...@@ -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( return new StockList(
keySalt: tab,
stocks: stocks.toList(), stocks: stocks.toList(),
onAction: _buyStock, onAction: _buyStock,
onOpen: (Stock stock, Key arrowKey) { onOpen: (Stock stock, Key arrowKey) {
...@@ -214,6 +215,13 @@ class StockHomeState extends State<StockHome> { ...@@ -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 const List<String> portfolioSymbols = const <String>["AAPL","FIZZ", "FIVE", "FLAT", "ZINC", "ZNGA"];
static GlobalKey searchFieldKey = new GlobalKey(); static GlobalKey searchFieldKey = new GlobalKey();
...@@ -259,13 +267,6 @@ class StockHomeState extends State<StockHome> { ...@@ -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; double _viewWidth = 100.0;
void _handleSizeChanged(Size newSize) { void _handleSizeChanged(Size newSize) {
setState(() { setState(() {
...@@ -282,14 +283,14 @@ class StockHomeState extends State<StockHome> { ...@@ -282,14 +283,14 @@ class StockHomeState extends State<StockHome> {
onSizeChanged: _handleSizeChanged, onSizeChanged: _handleSizeChanged,
child: new TabBarView<StockHomeTab>( child: new TabBarView<StockHomeTab>(
selection: _tabBarSelection, selection: _tabBarSelection,
items: [StockHomeTab.market, StockHomeTab.portfolio], items: <StockHomeTab>[StockHomeTab.market, StockHomeTab.portfolio],
itemExtent: _viewWidth, itemExtent: _viewWidth,
itemBuilder: (BuildContext context, StockHomeTab tab, _) { itemBuilder: (BuildContext context, StockHomeTab tab, _) {
switch (tab) { switch (tab) {
case StockHomeTab.market: case StockHomeTab.market:
return buildStockTab(context, tab, config.symbols); return _buildStockTab(context, tab, config.symbols);
case StockHomeTab.portfolio: case StockHomeTab.portfolio:
return buildStockTab(context, tab, portfolioSymbols); return _buildStockTab(context, tab, portfolioSymbols);
default: default:
assert(false); assert(false);
} }
......
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
part of stocks; part of stocks;
class StockList extends StatelessComponent { 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 List<Stock> stocks;
final StockRowActionCallback onOpen; final StockRowActionCallback onOpen;
final StockRowActionCallback onShow; final StockRowActionCallback onShow;
...@@ -18,6 +19,7 @@ class StockList extends StatelessComponent { ...@@ -18,6 +19,7 @@ class StockList extends StatelessComponent {
itemExtent: StockRow.kHeight, itemExtent: StockRow.kHeight,
itemBuilder: (BuildContext context, Stock stock, int index) { itemBuilder: (BuildContext context, Stock stock, int index) {
return new StockRow( return new StockRow(
keySalt: keySalt,
stock: stock, stock: stock,
onPressed: onOpen, onPressed: onOpen,
onDoubleTap: onShow, onDoubleTap: onShow,
......
...@@ -7,18 +7,22 @@ part of stocks; ...@@ -7,18 +7,22 @@ part of stocks;
enum StockRowPartKind { arrow } enum StockRowPartKind { arrow }
class StockRowPartKey extends Key { 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 Stock stock;
final StockRowPartKind part; final StockRowPartKind part;
bool operator ==(dynamic other) { bool operator ==(dynamic other) {
if (other is! StockRowPartKey) if (identical(this, other))
return true;
if (other.runtimeType != runtimeType)
return false; return false;
final StockRowPartKey typedOther = other; final StockRowPartKey typedOther = other;
return stock == typedOther.stock && return keySalt == typedOther.keySalt
part == typedOther.part; && stock == typedOther.stock
&& part == typedOther.part;
} }
int get hashCode => 37 * (37 * (373) + identityHashCode(stock)) + identityHashCode(part); int get hashCode => 37 * (37 * (37 * (373) + identityHashCode(keySalt)) + identityHashCode(stock)) + identityHashCode(part);
String toString() => '[StockRowPartKey ${stock.symbol}:${part.toString().split(".")[1]})]'; String toString() => '[$runtimeType ${keySalt.toString().split(".")[1]}:${stock.symbol}:${part.toString().split(".")[1]}]';
} }
typedef void StockRowActionCallback(Stock stock, Key arrowKey); typedef void StockRowActionCallback(Stock stock, Key arrowKey);
...@@ -26,11 +30,12 @@ typedef void StockRowActionCallback(Stock stock, Key arrowKey); ...@@ -26,11 +30,12 @@ typedef void StockRowActionCallback(Stock stock, Key arrowKey);
class StockRow extends StatelessComponent { class StockRow extends StatelessComponent {
StockRow({ StockRow({
Stock stock, Stock stock,
Object keySalt,
this.onPressed, this.onPressed,
this.onDoubleTap, this.onDoubleTap,
this.onLongPressed this.onLongPressed
}) : this.stock = stock, }) : this.stock = stock,
_arrowKey = new StockRowPartKey(stock, StockRowPartKind.arrow), _arrowKey = new StockRowPartKey(keySalt, stock, StockRowPartKind.arrow),
super(key: new ObjectKey(stock)); super(key: new ObjectKey(stock));
final Stock stock; final Stock stock;
......
...@@ -88,6 +88,8 @@ class HeroController extends NavigatorObserver { ...@@ -88,6 +88,8 @@ class HeroController extends NavigatorObserver {
} }
Set<Key> _getMostValuableKeys() { Set<Key> _getMostValuableKeys() {
assert(_from != null);
assert(_to != null);
Set<Key> result = new Set<Key>(); Set<Key> result = new Set<Key>();
if (_from.settings.mostValuableKeys != null) if (_from.settings.mostValuableKeys != null)
result.addAll(_from.settings.mostValuableKeys); result.addAll(_from.settings.mostValuableKeys);
......
...@@ -107,7 +107,19 @@ class Hero extends StatefulComponent { ...@@ -107,7 +107,19 @@ class Hero extends StatefulComponent {
assert(tag != null); assert(tag != null);
Key key = hero.widget.key; Key key = hero.widget.key;
final Map<Key, HeroState> tagHeroes = heroes.putIfAbsent(tag, () => <Key, HeroState>{}); 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; tagHeroes[key] = hero.state;
} }
element.visitChildren(visitor); 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