import 'package:flutter/material.dart'; class CustomTabView extends StatefulWidget { final int itemCount; final IndexedWidgetBuilder tabBuilder; final IndexedWidgetBuilder pageBuilder; final ValueChanged onPositionChange; final ValueChanged onScroll; final int initPosition; CustomTabView({ @required this.itemCount, @required this.tabBuilder, @required this.pageBuilder, this.onPositionChange, this.onScroll, this.initPosition, }); @override _CustomTabsState createState() => _CustomTabsState(); } class _CustomTabsState extends State with TickerProviderStateMixin { TabController controller; int _currentCount; int _currentPosition; @override void initState() { _currentPosition = widget.initPosition ?? 0; controller = TabController( length: widget.itemCount, vsync: this, initialIndex: _currentPosition, ); controller.addListener(onPositionChange); controller.animation.addListener(onScroll); _currentCount = widget.itemCount; super.initState(); } @override void didUpdateWidget(CustomTabView oldWidget) { if (_currentCount != widget.itemCount) { controller.animation.removeListener(onScroll); controller.removeListener(onPositionChange); controller.dispose(); if (widget.initPosition != null) { _currentPosition = widget.initPosition; } if (_currentPosition > widget.itemCount - 1) { _currentPosition = widget.itemCount - 1; _currentPosition = _currentPosition < 0 ? 0 : _currentPosition; if (widget.onPositionChange is ValueChanged) { WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { widget.onPositionChange(_currentPosition); } }); } } _currentCount = widget.itemCount; setState(() { controller = TabController( length: widget.itemCount, vsync: this, initialIndex: _currentPosition, ); controller.addListener(onPositionChange); controller.animation.addListener(onScroll); }); } else if (widget.initPosition != null) { controller.animateTo(widget.initPosition); } super.didUpdateWidget(oldWidget); } @override void dispose() { controller.animation.removeListener(onScroll); controller.removeListener(onPositionChange); controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Container( alignment: Alignment.centerLeft, height: 50.0, padding: EdgeInsets.all(10.0), child: TabBar( indicatorSize: TabBarIndicatorSize.label, labelPadding: EdgeInsets.symmetric(horizontal: 5.0), indicatorPadding: EdgeInsets.symmetric(horizontal: 5.0), isScrollable: true, controller: controller, labelColor: Colors.white, unselectedLabelColor: Colors.grey[700], indicator: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(15)), color: Theme.of(context).accentColor, ), tabs: List.generate( widget.itemCount, (index) => widget.tabBuilder(context, index), ), ), ), Expanded( child: TabBarView( controller: controller, children: List.generate( widget.itemCount, (index) => widget.pageBuilder(context, index), ), ), ), ], ); } onPositionChange() { if (!controller.indexIsChanging) { _currentPosition = controller.index; if (widget.onPositionChange is ValueChanged) { widget.onPositionChange(_currentPosition); } } } onScroll() { if (widget.onScroll is ValueChanged) { widget.onScroll(controller.animation.value); } } }