一般项目中都有这种多标签页的情况,正常使用 ListView.builder 构建横向滚动的视图时,通过绑定的 controller 可以控制 listView 的偏移量,但是如果子控件长短不一致,计算起来就很麻烦了。
我们可以通过给每一个子 widget 绑定 GlobalKey,并保存到数组中,在点击切换的时候,获取到点击控件对应的 key,再调用 Scrollable.ensureVisiable 方法就能很方便的实现上述效果。
完整代码如下
class _HomePageState extends State<HomePage> {
List<String> titles = [];
List<GlobalKey> itemKeys = [];
int selectedIndex = 0;
@override
void initState() {
List<String> res = [];
for (var i = 0; i < 10; i++) {
res.add('index-$i');
itemKeys.add(GlobalKey());
}
setState(() {
titles = res;
});
super.initState();
}
void scrollToItem(int index) {
final context = itemKeys[index].currentContext!;
Scrollable.ensureVisible(
context,
alignment: 0.5, // 控制出现的位置
duration: const Duration(milliseconds: 300), // 动画时长
);
}
@override
Widget build(BuildContext context) {
return Center(
child: SizedBox(
height: 32,
child: ListView.builder(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 20),
itemCount: titles.length,
itemBuilder: (context, index) {
return Container(
key: itemKeys[index],
decoration: BoxDecoration(
color: selectedIndex == index ? Colors.blue : Colors.grey,
borderRadius: const BorderRadius.all(Radius.circular(15)),
),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
margin: const EdgeInsets.symmetric(horizontal: 5),
child: GestureDetector(
onTap: () {
setState(() {
selectedIndex = index;
});
scrollToItem(index);
},
child: Text(
titles[index],
style: TextStyle(
color: selectedIndex == index
? Colors.white
: Colors.grey.shade50),
),
),
);
},
),
),
);
}
}