147 lines
4.0 KiB
Dart
147 lines
4.0 KiB
Dart
|
import 'package:flutter/material.dart';
|
||
|
|
||
|
class CustomTabView extends StatefulWidget {
|
||
|
final int itemCount;
|
||
|
final IndexedWidgetBuilder tabBuilder;
|
||
|
final IndexedWidgetBuilder pageBuilder;
|
||
|
final ValueChanged<int> onPositionChange;
|
||
|
final ValueChanged<double> 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<CustomTabView>
|
||
|
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<int>) {
|
||
|
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: <Widget>[
|
||
|
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<int>) {
|
||
|
widget.onPositionChange(_currentPosition);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
onScroll() {
|
||
|
if (widget.onScroll is ValueChanged<double>) {
|
||
|
widget.onScroll(controller.animation.value);
|
||
|
}
|
||
|
}
|
||
|
}
|