|
|
|
|
|
|
Key key = null, |
|
|
|
string text = null, |
|
|
|
Widget icon = null, |
|
|
|
EdgeInsets iconMargin = null, |
|
|
|
Widget child = null |
|
|
|
) : base(key: key) { |
|
|
|
D.assert(text != null || child != null || icon != null); |
|
|
|
|
|
|
this.child = child; |
|
|
|
this.iconMargin = iconMargin ?? EdgeInsets.only(bottom: 10.0f); |
|
|
|
} |
|
|
|
|
|
|
|
public readonly string text; |
|
|
|
|
|
|
public readonly Widget icon; |
|
|
|
|
|
|
|
public readonly EdgeInsets iconMargin; |
|
|
|
|
|
|
|
Widget _buildLabelText() { |
|
|
|
return child ?? new Text(text, softWrap: false, overflow: TextOverflow.fade); |
|
|
|
|
|
|
children: new List<Widget> { |
|
|
|
new Container( |
|
|
|
child: icon, |
|
|
|
margin: EdgeInsets.only(bottom: 10.0f) |
|
|
|
margin: iconMargin |
|
|
|
), |
|
|
|
_buildLabelText() |
|
|
|
} |
|
|
|
|
|
|
ThemeData themeData = Theme.of(context); |
|
|
|
TabBarTheme tabBarTheme = TabBarTheme.of(context); |
|
|
|
Animation<float> animation = (Animation<float>) listenable; |
|
|
|
|
|
|
|
|
|
|
|
?? themeData.primaryTextTheme.body2).copyWith(inherit: true); |
|
|
|
?? themeData.primaryTextTheme.bodyText1).copyWith(inherit: true); |
|
|
|
?? tabBarTheme.unselectedLabelStyle |
|
|
|
?? labelStyle |
|
|
|
?? themeData.primaryTextTheme.body2).copyWith(inherit: true); |
|
|
|
?? tabBarTheme.unselectedLabelStyle |
|
|
|
?? labelStyle |
|
|
|
?? themeData.primaryTextTheme.bodyText1).copyWith(inherit: true); |
|
|
|
Color selectedColor = labelColor ?? tabBarTheme.labelColor ?? themeData.primaryTextTheme.body2.color; |
|
|
|
Color selectedColor = labelColor ?? tabBarTheme.labelColor ?? themeData.primaryTextTheme.bodyText1.color; |
|
|
|
Color unselectedColor = unselectedLabelColor ?? |
|
|
|
tabBarTheme.unselectedLabelColor ?? selectedColor.withAlpha(0xB2); |
|
|
|
Color color = selected |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
static bool _tabOffsetsEqual(List<float> a, List<float> b) { |
|
|
|
if (a?.Count != b?.Count) { |
|
|
|
if (a == null || b == null || a.Count != b.Count) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
get { return controller.animation; } |
|
|
|
} |
|
|
|
|
|
|
|
public override void removeStatusListener(AnimationStatusListener listener) { |
|
|
|
if (parent != null) { |
|
|
|
base.removeStatusListener(listener); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public override void removeListener(VoidCallback listener) { |
|
|
|
if (parent != null) { |
|
|
|
base.removeListener(listener); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public override float value { |
|
|
|
get { return TabsUtils._indexChangeProgress(controller); } |
|
|
|
} |
|
|
|
|
|
|
get { return controller.animation; } |
|
|
|
} |
|
|
|
|
|
|
|
public override void removeStatusListener(AnimationStatusListener listener) { |
|
|
|
if (parent != null) { |
|
|
|
base.removeStatusListener(listener); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public override void removeListener(VoidCallback listener) { |
|
|
|
if (parent != null) { |
|
|
|
base.removeListener(listener); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public override float value { |
|
|
|
get { |
|
|
|
D.assert(!controller.indexIsChanging); |
|
|
|
|
|
|
foreach (Widget item in tabs) { |
|
|
|
if (item is Tab) { |
|
|
|
Tab tab = (Tab) item; |
|
|
|
if (tab.text != null && tab.icon != null) { |
|
|
|
if ((tab.text != null || tab.child != null) && tab.icon != null) { |
|
|
|
return Size.fromHeight(TabsUtils._kTextAndIconTabHeight + indicatorWeight); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
bool _controllerIsValid { |
|
|
|
get { return _controller?.animation != null; } |
|
|
|
} |
|
|
|
|
|
|
|
void _updateTabController() { |
|
|
|
TabController newController = widget.controller ?? DefaultTabController.of(context); |
|
|
|
D.assert(() => { |
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
}); |
|
|
|
D.assert(() => { |
|
|
|
if (newController.length != widget.tabs.Count) { |
|
|
|
throw new UIWidgetsError( |
|
|
|
$"Controller's length property {newController.length} does not match the\n" + |
|
|
|
$"number of tab elements {widget.tabs.Count} present in TabBar's tabs property." |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
}); |
|
|
|
if (_controller != null) { |
|
|
|
if (_controllerIsValid) { |
|
|
|
_controller.animation.removeListener(_handleTabControllerAnimationTick); |
|
|
|
_controller.removeListener(_handleTabControllerTick); |
|
|
|
} |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
void _initIndicatorPainter() { |
|
|
|
_indicatorPainter = _controller == null |
|
|
|
_indicatorPainter = (!_controllerIsValid) |
|
|
|
? null |
|
|
|
: new _IndicatorPainter( |
|
|
|
controller: _controller, |
|
|
|
|
|
|
|
|
|
|
public override void dispose() { |
|
|
|
_indicatorPainter.dispose(); |
|
|
|
if (_controller != null) { |
|
|
|
if (_controllerIsValid) { |
|
|
|
_controller = null; |
|
|
|
base.dispose(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public override Widget build(BuildContext context) { |
|
|
|
D.assert(material_.debugCheckHasMaterialLocalizations(context)); |
|
|
|
D.assert(() => { |
|
|
|
if (_controller.length != widget.tabs.Count) { |
|
|
|
throw new UIWidgetsError( |
|
|
|
$"Controller's length property {_controller.length} does not match the\n" + |
|
|
|
$"number of tab elements {widget.tabs.Count} present in TabBar's tabs property." |
|
|
|
); |
|
|
|
} |
|
|
|
return true; |
|
|
|
}); |
|
|
|
if (_controller.length == 0) { |
|
|
|
return new Container( |
|
|
|
height: TabsUtils._kTabHeight + widget.indicatorWeight |
|
|
|
|
|
|
TabController _controller; |
|
|
|
PageController _pageController; |
|
|
|
List<Widget> _children; |
|
|
|
List<Widget> _childrenWithKey; |
|
|
|
|
|
|
|
bool _controllerIsValid { |
|
|
|
get { return _controller?.animation != null; } |
|
|
|
} |
|
|
|
|
|
|
|
void _updateTabController() { |
|
|
|
TabController newController = widget.controller ?? DefaultTabController.of(context); |
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
}); |
|
|
|
D.assert(() => { |
|
|
|
if (newController.length != widget.children.Count) { |
|
|
|
throw new UIWidgetsError( |
|
|
|
$"Controller's length property {newController.length} does not match the\n" + |
|
|
|
$"number of elements {widget.children.Count} present in TabBarView's children property." |
|
|
|
); |
|
|
|
} |
|
|
|
return true; |
|
|
|
}); |
|
|
|
if (_controller != null) { |
|
|
|
if (_controllerIsValid) { |
|
|
|
_controller.animation.removeListener(_handleTabControllerAnimationTick); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public override void initState() { |
|
|
|
base.initState(); |
|
|
|
_children = widget.children; |
|
|
|
_updateChildren(); |
|
|
|
} |
|
|
|
|
|
|
|
public override void didChangeDependencies() { |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (widget.children != _oldWidget.children && _warpUnderwayCount == 0) { |
|
|
|
_children = widget.children; |
|
|
|
_updateChildren(); |
|
|
|
if (_controller != null) { |
|
|
|
if (_controllerIsValid) { |
|
|
|
_controller = null; |
|
|
|
void _updateChildren() { |
|
|
|
_children = widget.children; |
|
|
|
_childrenWithKey = KeyedSubtree.ensureUniqueKeysForList(widget.children); |
|
|
|
} |
|
|
|
|
|
|
|
void _handleTabControllerAnimationTick() { |
|
|
|
if (_warpUnderwayCount > 0 || !_controller.indexIsChanging) { |
|
|
|
return; |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
D.assert((_currentIndex.Value - previousIndex).abs() > 1); |
|
|
|
int initialPage = 0; |
|
|
|
int initialPage = _currentIndex.Value > previousIndex |
|
|
|
? _currentIndex.Value - 1 |
|
|
|
: _currentIndex.Value + 1; |
|
|
|
List<Widget> originalChildren = _childrenWithKey; |
|
|
|
|
|
|
|
_children = new List<Widget>(widget.children); |
|
|
|
if (_currentIndex > previousIndex) { |
|
|
|
_children[_currentIndex.Value - 1] = _children[previousIndex]; |
|
|
|
initialPage = _currentIndex.Value - 1; |
|
|
|
} |
|
|
|
else { |
|
|
|
_children[_currentIndex.Value + 1] = _children[previousIndex]; |
|
|
|
initialPage = _currentIndex.Value + 1; |
|
|
|
} |
|
|
|
|
|
|
|
_childrenWithKey = new List<Widget>(_childrenWithKey); |
|
|
|
Widget temp = _childrenWithKey[initialPage]; |
|
|
|
_childrenWithKey[initialPage] = _childrenWithKey[previousIndex]; |
|
|
|
_childrenWithKey[previousIndex] = temp; |
|
|
|
}); |
|
|
|
|
|
|
|
_pageController.jumpToPage(initialPage); |
|
|
|
|
|
|
|
|
|
|
setState(() => { |
|
|
|
_warpUnderwayCount -= 1; |
|
|
|
_children = widget.children; |
|
|
|
if (widget.children != _children) { |
|
|
|
_updateChildren(); |
|
|
|
} |
|
|
|
else { |
|
|
|
_childrenWithKey = originalChildren; |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
return Future.value(); |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
public override Widget build(BuildContext context) { |
|
|
|
D.assert(() => { |
|
|
|
if (_controller.length != widget.children.Count) { |
|
|
|
throw new UIWidgetsError( |
|
|
|
$"Controller's length property {_controller.length} does not match the\n" + |
|
|
|
$"number of tabs {widget.children.Count} present in TabBar's tabs property." |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
}); |
|
|
|
|
|
|
|
return new NotificationListener<ScrollNotification>( |
|
|
|
onNotification: _handleScrollNotification, |
|
|
|
child: new PageView( |
|
|
|
|
|
|
? TabsUtils._kTabBarViewPhysics |
|
|
|
: TabsUtils._kTabBarViewPhysics.applyTo(widget.physics), |
|
|
|
children: _children |
|
|
|
children: _childrenWithKey |
|
|
|
) |
|
|
|
); |
|
|
|
} |
|
|
|