diff --git a/example/lib/default_appbar_demo.dart b/example/lib/default_appbar_demo.dart index 74d6fe8..a703558 100644 --- a/example/lib/default_appbar_demo.dart +++ b/example/lib/default_appbar_demo.dart @@ -163,11 +163,12 @@ class _State extends State color: Colors.white, tooltip: "convex button example", onPressed: () => Navigator.of(context).pushNamed('/fab'), - ), IconButton( - icon: Icon(Icons.radio_button_checked), + ), + IconButton( + icon: Icon(Icons.looks_two), color: Colors.white, tooltip: "change tab by controller", - onPressed: (){ + onPressed: () { _tabController?.animateTo(2); }, ) @@ -195,7 +196,11 @@ class _State extends State onTap: (int i) => debugPrint('select index=$i'), ) : ConvexAppBar.badge( - {3: _badge!.text, 4: Icons.assistant_photo, 2: Colors.redAccent}, + { + 3: _badge!.text, + 4: Icons.assistant_photo, + 2: Colors.redAccent + }, badgePadding: _badge!.padding, badgeColor: _badge!.badgeColor, badgeBorderRadius: _badge!.borderRadius, @@ -221,9 +226,7 @@ class _State extends State }); } - void _onNothing(ChoiceValue? value) { - - } + void _onNothing(ChoiceValue? value) {} void _onStyleChanged(ChoiceValue? value) { if (value == null) { diff --git a/lib/src/bar.dart b/lib/src/bar.dart index 3dafca3..a00018e 100644 --- a/lib/src/bar.dart +++ b/lib/src/bar.dart @@ -361,6 +361,7 @@ class ConvexAppBar extends StatefulWidget { class ConvexAppBarState extends State with TickerProviderStateMixin { int? _currentIndex; + int? get currentIndex => _currentIndex; int _warpUnderwayCount = 0; Animation? _animation; AnimationController? _animationController; @@ -403,17 +404,14 @@ class ConvexAppBarState extends State if (c == null) { return; } - // Workaround for TabController, see https://github.com/hacktons/convex_bottom_bar/issues/59 - var _diff = (c.index - _currentIndex!).abs(); - if (_diff == 1) { - if (_blockEvent(c.index)) return; - final previousIndex = c.previousIndex; - final index = c.index; - _warpUnderwayCount += 1; - await animateTo(index, from: previousIndex); - _warpUnderwayCount -= 1; - return Future.value(); - } + if (_blockEvent(c.index)) return; + final previousIndex = c.previousIndex; + final index = c.index; + // Counter to avoid repeat calls to animateTo in the middle of a transition. + _warpUnderwayCount += 1; + await animateTo(index, from: previousIndex); + _warpUnderwayCount -= 1; + return Future.value(); } /// change active tab index; can be used with [PageView]. @@ -465,7 +463,7 @@ class ConvexAppBarState extends State super.dispose(); } - TabController? get _takeControllerRef { + TabController? get _currentControllerRef { if (widget.disableDefaultTabController == true) { return widget.controller; } @@ -473,7 +471,7 @@ class ConvexAppBarState extends State } void _updateTabController() { - final newController = _takeControllerRef; + final newController = _currentControllerRef; assert(() { if (newController != null && widget.controller == null && @@ -505,7 +503,7 @@ class ConvexAppBarState extends State @override void didChangeDependencies() { super.didChangeDependencies(); - if (_controller != _takeControllerRef) { + if (_controller != _currentControllerRef) { _updateTabController(); _resetState(); } @@ -625,8 +623,13 @@ class ConvexAppBarState extends State void _onTabClick(int i) { if (_blockEvent(i)) return; - animateTo(i); - _controller?.animateTo(i); + if (_controller == null) { + animateTo(i); + } else { + // animation listener [_handleTabControllerAnimationTick] will drive the + // internal animateTo() via [_warpToCurrentIndex]. + _controller!.animateTo(i); + } widget.onTap?.call(i); } diff --git a/test/widget_test.dart b/test/widget_test.dart index 036eaa3..6731a80 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -188,7 +188,7 @@ void main() { testWidgets('Test tab controller', (WidgetTester tester) async { var controller = TabController(length: 3, vsync: TestVSync(), initialIndex: 2); - var key = GlobalKey(debugLabel: 'appbar'); + var key = GlobalKey(debugLabel: 'appbar'); var appbar = ConvexAppBar.builder( key: key, controller: controller, @@ -196,31 +196,62 @@ void main() { count: 3, top: -20, onTap: (i) { - assert(i == 1); + expect(i, 1); }, ); await tester.pumpWidget( - material(appbar), + // material(appbar), + material(Scaffold( + body: TabBarView( + controller: controller, + children: const [ + Center(child: Text('CHILD 0')), + Center(child: Text('CHILD 1')), + Center(child: Text('CHILD 2')), + ], + ), + bottomNavigationBar: appbar, + )), Duration(milliseconds: 300), ); + expect(key.currentState?.currentIndex, 2); expect(find.text('TAB 0'), findsOneWidget); expect(find.text('TAB 1'), findsOneWidget); + expect(find.text('TAB 2'), findsOneWidget); + expect(find.text('CHILD 2'), findsOneWidget); + await tester.tap(find.text('TAB 1')); await tester.tap(find.text('TAB 1')); await tester.pumpAndSettle(Duration(milliseconds: 300)); - await tester.startGesture(Offset(0, 100)).then((g) { - return g.moveTo(Offset(500, 100)); - }); - await tester.startGesture(Offset(0, 100)).then((g) { - return g.moveTo(Offset(100, 100)); - }); - controller.index = 1; + expect(controller.index, 1); + expect(key.currentState?.currentIndex, 1); + + await tester.drag(find.text('CHILD 1'), Offset(1000, 0)); await tester.pumpAndSettle(Duration(milliseconds: 300)); + expect(controller.index, 0); + expect(key.currentState?.currentIndex, 0); + + controller.animateTo(2); + await tester.pumpAndSettle(Duration(milliseconds: 300)); + expect(controller.index, 2); + expect(key.currentState?.currentIndex, 2); + + // Test a custom controller accidentally used with the DefaultTabController + controller.index = 2; await tester.pumpWidget( material(DefaultTabController( - initialIndex: 2, - length: 3, - child: ConvexAppBar.builder( + initialIndex: 0, + length: 3, + child: material(Scaffold( + body: TabBarView( + controller: controller, + children: const [ + Center(child: Text('CHILD 0')), + Center(child: Text('CHILD 1')), + Center(child: Text('CHILD 2')), + ], + ), + bottomNavigationBar: ConvexAppBar.builder( key: key, controller: controller, itemBuilder: Builder(), @@ -229,10 +260,16 @@ void main() { onTap: (i) { assert(i == 1); }, - ))), + ), + )), + )), Duration(milliseconds: 300), ); - controller.index = 1; + expect(key.currentState?.currentIndex, 2); + await tester.flingFrom(Offset(0, 100), const Offset(600, 100), 10000.0); + await tester.pumpAndSettle(Duration(milliseconds: 300)); + expect(controller.index, 1); + expect(key.currentState?.currentIndex, 1); }); testWidgets('Add badge on AppBar', (WidgetTester tester) async {