Skip to content

Commit

Permalink
add TabController to make AppBar work with PageView
Browse files Browse the repository at this point in the history
  • Loading branch information
avenwu committed Feb 17, 2020
1 parent 93e8f1d commit c1b3c01
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 30 deletions.
26 changes: 24 additions & 2 deletions example/lib/custom_appbar_sample.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ class CustomAppBarDemo extends StatefulWidget {
}
}

class _State extends State<CustomAppBarDemo> {
class _State extends State<CustomAppBarDemo>
with SingleTickerProviderStateMixin {
List<TabItem> items = <TabItem>[
TabItem(icon: Icons.home, title: 'Home'),
TabItem(icon: Icons.map, title: 'Discovery'),
Expand Down Expand Up @@ -40,15 +41,36 @@ class _State extends State<CustomAppBarDemo> {
Color(0xFF607D8B),
];
Color _tabBackgroundColor = paletteColors[5];
TabController _tabController;

@override
void initState() {
super.initState();
_tabController =
TabController(initialIndex: 2, length: items.length, vsync: this);
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Custom ConvexAppBar')),
body: paletteBody(),
body: TabBarView(
controller: _tabController,
children: items
.map((i) => i.title == 'Discovery'
? paletteBody()
: Center(
child: Text(
'<\t\t${i.title}\t\t>',
style: Theme.of(context).textTheme.display1,
)))
.toList(growable: false),
),
bottomNavigationBar: ConvexAppBar.builder(
itemBuilder: _CustomBuilder(items, _tabBackgroundColor),
count: items.length,
tabController: _tabController,
initialActiveIndex: 1,
backgroundColor: _tabBackgroundColor,
style: TabStyle.fixed,
),
Expand Down
27 changes: 24 additions & 3 deletions example/lib/default_appbar_demo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ class DefaultAppBarDemo extends StatefulWidget {
}
}

class _State extends State<DefaultAppBarDemo> {
class _State extends State<DefaultAppBarDemo>
with SingleTickerProviderStateMixin {
static const INDEX_PUBLISH = 2;
static const kStyles = [
ChoiceValue<TabStyle>(
Expand Down Expand Up @@ -82,6 +83,13 @@ class _State extends State<DefaultAppBarDemo> {
Color _babColor = Data.namedColors.first.color;
Gradient _gradient = Data.gradients.first;
Badge _badge;
TabController _tabController;

@override
void initState() {
super.initState();
_tabController = TabController(length: _tabItems.value.length, vsync: this);
}

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -118,15 +126,27 @@ class _State extends State<DefaultAppBarDemo> {
)
],
),
body: ListView(children: options),
body: TabBarView(
controller: _tabController,
children: _tabItems.value
.map((i) => i.title == 'Home'
? ListView(
children: options,
)
: Center(
child: Text(
'${i.title}',
style: Theme.of(context).textTheme.display1,
)))
.toList(growable: false)),
bottomNavigationBar: _badge == null
? ConvexAppBar(
items: _tabItems.value,
initialActiveIndex: 2,
style: _style.value,
curve: _curve.value,
backgroundColor: _babColor,
gradient: _gradient,
tabController: _tabController,
onTap: (int i) => debugPrint('select index=$i'),
)
: ConvexAppBar.badge(
Expand All @@ -139,6 +159,7 @@ class _State extends State<DefaultAppBarDemo> {
curve: _curve.value,
backgroundColor: _babColor,
gradient: _gradient,
tabController: _tabController,
onTap: (int i) => debugPrint('select index=$i'),
),
);
Expand Down
97 changes: 73 additions & 24 deletions lib/src/bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ class ConvexAppBar extends StatefulWidget {
/// Tab Click handler
final GestureTapIndexCallback onTap;

/// Tab controller to work with [TabBarView] or [PageView]
final TabController tabController;

/// Color of the AppBar
final Color backgroundColor;

Expand All @@ -88,7 +91,7 @@ class ConvexAppBar extends StatefulWidget {
/// ![](https://github.com/hacktons/convex_bottom_bar/raw/master/doc/appbar-gradient.gif)
final Gradient gradient;

/// The initial active index, default as 0 if not provided;
/// The initial active index, you can config initialIndex of [TabController] if work with [TabBarView] or [PageView];
final int initialActiveIndex;

/// Tab count
Expand Down Expand Up @@ -131,6 +134,7 @@ class ConvexAppBar extends StatefulWidget {
@required List<TabItem> items,
int initialActiveIndex,
GestureTapIndexCallback onTap,
TabController tabController,
Color color,
Color activeColor,
Color backgroundColor,
Expand All @@ -153,6 +157,7 @@ class ConvexAppBar extends StatefulWidget {
curve: curve ?? Curves.easeInOut,
),
onTap: onTap,
tabController: tabController,
backgroundColor: backgroundColor ?? Colors.blue,
count: items.length,
initialActiveIndex: initialActiveIndex,
Expand All @@ -173,6 +178,7 @@ class ConvexAppBar extends StatefulWidget {
@required this.count,
this.initialActiveIndex,
this.onTap,
this.tabController,
this.backgroundColor,
this.gradient,
this.height,
Expand Down Expand Up @@ -220,6 +226,7 @@ class ConvexAppBar extends StatefulWidget {
List<TabItem> items,
int initialActiveIndex,
GestureTapIndexCallback onTap,
TabController tabController,
Color color,
Color activeColor,
Color backgroundColor,
Expand All @@ -246,6 +253,7 @@ class ConvexAppBar extends StatefulWidget {
items: items,
initialActiveIndex: initialActiveIndex,
onTap: onTap,
tabController: tabController,
color: color,
activeColor: activeColor,
backgroundColor: backgroundColor,
Expand Down Expand Up @@ -278,17 +286,46 @@ abstract class DelegateBuilder {
}

class _State extends State<ConvexAppBar> with TickerProviderStateMixin {
int _currentSelectedIndex;
int _currentIndex;
Animation<double> _animation;
AnimationController _controller;
TabController _tabController;

@override
void initState() {
_currentSelectedIndex = widget.initialActiveIndex ?? 0;
super.initState();
_updateTabController();
_currentIndex = widget.initialActiveIndex ?? _tabController?.index ?? 0;

/// When both ConvexAppBar and TabController are configured with initial index, there can be conflict;
/// We use ConvexAppBar's value;
if (widget.initialActiveIndex != null &&
_tabController != null &&
widget.initialActiveIndex != _tabController.index) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_tabController.index = _currentIndex;
});
}
if (!isFixed()) {
_initAnimation();
}
super.initState();
}

void _handleTabControllerAnimationTick({bool force = false}) {
if (!force && _tabController.indexIsChanging) {
return;
}
if (_tabController.index != _currentIndex) {
_warpToCurrentIndex(_tabController.index);
}
}

Future<void> _warpToCurrentIndex(int index) async {
_initAnimation(from: _currentIndex, to: index);
_controller?.forward();
setState(() {
_currentIndex = index;
});
}

Animation<double> _initAnimation({int from, int to}) {
Expand Down Expand Up @@ -317,14 +354,36 @@ class _State extends State<ConvexAppBar> with TickerProviderStateMixin {
super.dispose();
}

_updateTabController() {
final TabController newController =
widget.tabController ?? DefaultTabController.of(context);
_tabController?.removeListener(_handleTabControllerAnimationTick);
_tabController = newController;
_tabController?.addListener(_handleTabControllerAnimationTick);
}

@override
void didChangeDependencies() {
super.didChangeDependencies();
_updateTabController();
}

@override
void didUpdateWidget(ConvexAppBar oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.tabController != oldWidget.tabController) {
_updateTabController();
}
}

@override
Widget build(BuildContext context) {
// take care of iPhoneX' safe area at bottom edge
final double additionalBottomPadding =
math.max(MediaQuery.of(context).padding.bottom, 0.0);
var halfSize = widget.count ~/ 2;
final convexIndex = isFixed() ? halfSize : _currentSelectedIndex;
final active = isFixed() ? convexIndex == _currentSelectedIndex : true;
final convexIndex = isFixed() ? halfSize : _currentIndex;
final active = isFixed() ? convexIndex == _currentIndex : true;
return extend.Stack(
overflow: Overflow.visible,
alignment: Alignment.bottomCenter,
Expand All @@ -346,7 +405,7 @@ class _State extends State<ConvexAppBar> with TickerProviderStateMixin {
),
),
),
barContent(additionalBottomPadding),
_barContent(additionalBottomPadding),
Positioned.fill(
top: widget.top,
bottom: additionalBottomPadding,
Expand All @@ -355,12 +414,7 @@ class _State extends State<ConvexAppBar> with TickerProviderStateMixin {
alignment: Alignment((convexIndex - halfSize) / (halfSize), 0),
child: GestureDetector(
child: _newTab(convexIndex, active),
onTap: () {
_onTabClick(convexIndex);
setState(() {
_currentSelectedIndex = convexIndex;
});
},
onTap: () => _onTabClick(convexIndex),
)),
),
],
Expand All @@ -369,27 +423,22 @@ class _State extends State<ConvexAppBar> with TickerProviderStateMixin {

bool isFixed() => widget.itemBuilder.fixed();

Widget barContent(double paddingBottom) {
Widget _barContent(double paddingBottom) {
List<Widget> children = [];
// add placeholder Widget
var curveTabIndex = isFixed() ? widget.count ~/ 2 : _currentSelectedIndex;
var curveTabIndex = isFixed() ? widget.count ~/ 2 : _currentIndex;
for (var i = 0; i < widget.count; i++) {
if (i == curveTabIndex) {
children.add(Expanded(child: Container()));
continue;
}
var active = _currentSelectedIndex == i;
var active = _currentIndex == i;
Widget child = _newTab(i, active);
children.add(Expanded(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
child: child,
onTap: () {
_onTabClick(i);
setState(() {
_currentSelectedIndex = i;
});
},
onTap: () => _onTabClick(i),
)));
}

Expand All @@ -413,8 +462,8 @@ class _State extends State<ConvexAppBar> with TickerProviderStateMixin {
}

void _onTabClick(int i) {
_initAnimation(from: _currentSelectedIndex, to: i);
_controller?.forward();
_warpToCurrentIndex(i);
_tabController?.index = i;
if (widget.onTap != null) {
widget.onTap(i);
}
Expand Down
1 change: 0 additions & 1 deletion lib/src/style/transition_container.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ class _State extends State<TransitionContainer> with TickerProviderStateMixin {
animationController?.dispose();
_setAnimation();
} else {
debugPrint('update transition container ${toString()}');
if (widget.disableAnimateWhenUpdate == true) {
return;
}
Expand Down

0 comments on commit c1b3c01

Please sign in to comment.