划重点:
TabbarView内的根组件,
不要加AutomaticKeepAliveClientMixin
不要加AutomaticKeepAliveClientMixin
不要加AutomaticKeepAliveClientMixin
重要的事情说三遍
bug背景:
先说说页面结构,整体从上到下纵向排列,最顶端是标题栏,保持固定不动,标题下面是背景图片(可被折叠收缩起来),图片下面是tabbar,tabbar下面是TabbarView(等同于Android里面的ViewPager/vue里面的swiper,tabbar和tabbarView所需的数据均需从后端获取)
排查历程:
1.首先想到的是ListView和外部NestedScrollView滑动事件冲突了,但经过一番折腾也没效果
2.新建了个小项目用来试验,简单搭了个和前面差不多的页面结构,发现竟如此丝滑,仔细比对后,就仅差一个AutomaticKeepAliveClientMixin,具体原因暂未分析,但能确定的是,和build重绘脱不开干系,demo代码如下:
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
TabController? tabController;
ValueNotifier indexNotifier = ValueNotifier(0);
@override
void initState() {
super.initState();
_fetch();
}
void _fetch() async {
await Future.delayed(const Duration(seconds: 2));
tabController = TabController(length: 11, vsync: this);
indexNotifier.value = 100;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return [
SliverAppBar(
titleSpacing: 0,
pinned: true,
floating: false,
snap: false,
toolbarHeight: 0,
expandedHeight: 250,
elevation: 0.0,
// 移除阴影以实现透明效果
flexibleSpace: FlexibleSpaceBar(
// title: Text('Title'),
background: Container(
width: double.infinity,
height: 250,
color: Colors.blue.withOpacity(0.5),
), // 半透明背景
),
bottom: PreferredSize(
preferredSize: Size.fromHeight(60),
child: Container(
color: Colors.white,
height: 60,
),
),
),
];
},
body: ValueListenableBuilder(
valueListenable: indexNotifier,
builder: (context, itemCount, child) {
return TabBarView(
controller: tabController,
children: [
Container(
child: ListView.builder(
itemCount: itemCount,
itemExtent: 50,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return Center(
child: Text("${index}"),
);
},
),
),
Container(),
Container(),
Container(),
Container(),
Container(),
Container(),
Container(),
Container(),
Container(),
Container(),
],
);
}),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}