У меня есть страница со структурой типа NestedScrollView> PageView> ListView, проблема в том, что когда я прокручиваю внутри списка, другие списки прокручиваются одинаково, как будто они используют один и тот же контроллер прокрутки.
![enter image description here](https://i.stack.imgur.com/sURJu.png)
Я сделал демонстрацию ниже и проверил, что если нет NestedScrollView или AutomaticKeepAliveClientMixin, все будет в порядке. Кто-нибудь знает, как исправить?
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
static const List<String> tabs = ['tender', 'prequalification', 'winCandidate', 'winBid'];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: SafeArea(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
/// * Something else
Container(
width: MediaQuery.of(context).size.width,
height: 100.0,
color: Colors.orange,
child: Center(
child: Text(
'Something else...',
style: TextStyle(
color: Colors.black,
fontSize: 40.0,
),
),
),
),
/// * Nested ScrollView
Expanded(
child: NestedScrollView(
controller: ScrollController(),
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
child: SliverAppBar(
title: Text('NestedScrollView AppBar'),
expandedHeight: 90.0,
forceElevated: innerBoxIsScrolled,
),
),
];
},
body: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification _) => true,
/// * PageView
child: PageView(
physics: BouncingScrollPhysics(),
controller: PageController(),
onPageChanged: (int page) {},
children: <Widget>[
...List.generate(
4,
(int index) => BulletinPageView(
/// * to keep scroll position of each pageview
key: PageStorageKey<int>(index),
pageIndex: index,
),
),
],
),
),
),
),
],
),
),
);
}
}
///***
/// * each pageview content, inside the NestedScrollView
/// */
class BulletinPageView extends StatefulWidget {
const BulletinPageView({
Key key,
@required this.pageIndex,
}) : super(key: key);
final int pageIndex;
@override
_BulletinPageViewState createState() => _BulletinPageViewState();
}
class _BulletinPageViewState extends State<BulletinPageView>
/// * each time page changed, no need to refetch
with AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true;
Future<List<int>> _getDataList;
@override
void initState() {
super.initState();
_getDataList = _fetchDataList();
}
Future<List<int>> _fetchDataList() async {
await Future.delayed(Duration(milliseconds: 1500));
final int length = (Random().nextInt(5) + 1) * 20;
final List<int> dataList = List<int>.generate(length, (int i) => i);
return dataList;
}
@override
Widget build(BuildContext context) {
super.build(context);
return FutureBuilder(
future: _getDataList,
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.active:
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(),
);
case ConnectionState.done:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
return Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Center(
child: Icon(
Icons.autorenew,
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Center(
child: Text(
'PageView ${widget.pageIndex}',
style: TextStyle(
color: Colors.red,
),
),
),
),
Flexible(
fit: FlexFit.loose,
child: DataListView(
onNotification: _onNotification,
dataList: snapshot.data,
),
),
Center(
child: Icon(
Icons.cached,
),
),
],
);
default:
return Container();
}
},
);
}
bool _onNotification(ScrollNotification scrollNotification) {
if (scrollNotification is ScrollStartNotification) {
ScrollStartNotification(
context: context,
metrics: scrollNotification.metrics,
).dispatch(context);
} else if (scrollNotification is ScrollUpdateNotification) {
ScrollUpdateNotification(
context: context,
metrics: scrollNotification.metrics,
).dispatch(context);
} else if (scrollNotification is ScrollEndNotification) {
ScrollEndNotification(
context: context,
metrics: scrollNotification.metrics,
).dispatch(context);
}
// do something
return true;
}
}
///***
/// * most inside data listview
/// */
class DataListView extends StatelessWidget {
const DataListView({
Key key,
@required this.onNotification,
@required this.dataList,
}) : super(key: key);
final bool Function(ScrollNotification) onNotification;
final List<int> dataList;
@override
Widget build(BuildContext context) {
return NotificationListener<ScrollNotification>(
onNotification: onNotification,
child: ListView.builder(
physics: BouncingScrollPhysics(),
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: dataList.length,
itemBuilder: (BuildContext context, int index) => Card(
child: ListTile(
leading: Icon(Icons.album),
title: Text('No.${dataList[index]}'),
subtitle: Text('subtitle...'),
),
),
),
);
}
}