anchor.diff:      _performance
         ..progress = progress
         ..forward().then((_) {
  -        if (onChanged != null)
  -          onChanged();
  +        if (config.onChanged != null)
  +          config.onChanged(_index);
           _indexIsChanging = false;
         });
     }
   
     int get previousIndex => _previousIndex;
     int _previousIndex = 0;
  -}
  -
  -/// A tab strip, consisting of several TabLabels and a TabBarSelection.
  -/// The TabBarSelection can be used to link this to a TabBarView.
  +
  +  Widget build(BuildContext context) {
  +    return new _TabBarSelection(selection: this, child: config.child);
  +  }
  +}
  +
  +
  +/// Displays a horizontal row of tabs, one per label. If isScrollable is
  +/// true then each tab is as wide as needed for its label and the entire
  +/// [TabBar] is scrollable. Otherwise each tab gets an equal share of the
  +/// available space. A [TabBarSelection] widget ancestor must have been
  +/// built to enable saving and monitoring the selected tab.
   ///
   /// Tabs must always have an ancestor Material object.
   class TabBar extends Scrollable {
     TabBar({
       Key key,
       this.labels,
  -    this.selection,
       this.isScrollable: false
     }) : super(key: key, scrollDirection: ScrollDirection.horizontal) {
       assert(labels != null);
       assert(labels.length > 1);
  -    assert(selection != null);
  -    assert(selection.maxIndex == labels.length - 1);
     }
   
     final Iterable<TabLabel> labels;
  -  final TabBarSelection selection;
     final bool isScrollable;
   
     _TabBarState createState() => new _TabBarState();
   }
   
   class _TabBarState extends ScrollableState<TabBar> {
  +
  +  TabBarSelectionState _selection;
  +  bool _indexIsChanging = false;
  +
  +  int get _tabCount => config.labels.length;
  +
  +  void _addSelectionListeners(TabBarSelectionState selection) {
  +    if (selection != null) {
  +      selection._performance
  +        ..addStatusListener(_handleStatusChange)
  +        ..addListener(_handleProgressChange);
  +    }
  +  }
  +
  +  void _removeSelectionListeners(TabBarSelectionState selection) {
  +    if (selection != null) {
  +      selection._performance
  +        ..removeStatusListener(_handleStatusChange)
  +        ..removeListener(_handleProgressChange);
  +    }
  +  }
  +
     void initState() {
       super.initState();
       scrollBehavior.isScrollable = config.isScrollable;
  -    config.selection._performance
  -      ..addStatusListener(_handleStatusChange)
  -      ..addListener(_handleProgressChange);
     }
   
     void dispose() {
  -    config.selection._performance
  -      ..removeStatusListener(_handleStatusChange)
  -      ..removeListener(_handleProgressChange)
  -      ..stop();
  +    _removeSelectionListeners(_selection);
       super.dispose();
     }
  -
  -  Performance get _performance => config.selection._performance;
  -
  -  int get _tabCount => config.labels.length;
  -
  -  bool _indexIsChanging = false;
   
     void _handleStatusChange(PerformanceStatus status) {
       if (_tabCount == 0)
  
positive.code:     _viewportSize = newSize;
      _updateScrollBehavior();
      if (config.isScrollable)
        scrollTo(_centeredTabScrollOffset(config.selection.index), duration: _kTabBarScroll);
    }
  
    Widget buildContent(BuildContext context) {
      assert(config.labels != null && config.labels.isNotEmpty);
      assert(Material.of(context) != null);
  
  
negative.code:       progress = 0.0;
      else if (_previousIndex == 0)
        progress = _performance.progress;
      else if (_previousIndex == config.maxIndex)
        progress = 1.0 - _performance.progress;
      else if (_previousIndex < _index)
        progress = (_performance.progress - 0.5) * 2.0;
  