[英]How to listen scroll event for ListView that placed inside a ScrollView in Flutter?
I want to make e view in Flutter apps that contain a ListView
(or maybe many) inside a ScrollView
, and completed successfully.我想在 Flutter 应用程序中创建 e 视图,这些应用程序在ScrollView
中包含一个ListView
(或者可能很多),并成功完成。 But I have a problem with the scroll listener for loading the next page of data that I bind to the ListView.但是我在加载我绑定到 ListView 的下一页数据时滚动侦听器有问题。 Where should I place the scrollController
?我应该在哪里放置scrollController
? in the ListView
or the ScrollView
?在ListView
或ScrollView
? or how I solve this problem?或者我如何解决这个问题?
class HomeChildState extends State<HomeChild> {
HomeChildState() {
_categoryBloc = CategoryBloc();
_spotBloc = SpotBloc();
}
CategoryBloc _categoryBloc;
SpotBloc _spotBloc;
int page = 1;
int perPage = 4;
final ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
_categoryBloc.getCategories(1, 5);
_checkLocationPermission();
_scrollController.addListener(_onScroll);
}
void _onScroll() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
setState(() {
page += 1;
});
_spotBloc.getNearestSpot(page, perPage, _position.latitude, _position.longitude);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: const Size(null, 40),
child: Container(
padding: const EdgeInsets.only(left: 16, bottom: 12),
color: orangeGradient4,
child: const Text(
'Beranda',
style: TextStyle(
color: white1, fontWeight: FontWeight.bold, fontSize: 16),
),
),
),
body: CustomScrollView(
slivers: <Widget>[
SliverList(
delegate: SliverChildListDelegate(
<Widget>[
Column(
children: <Widget>[
Container(
height: 150,
width: MediaQuery.of(context).size.width,
alignment: Alignment.center,
decoration: BoxDecoration(
image: DecorationImage(
image: const AssetImage('assets/home_thumbnail.png'),
repeat: ImageRepeat.noRepeat,
alignment: FractionalOffset.center,
fit: BoxFit.cover)),
),
Padding(
padding:
const EdgeInsets.only(top: 12, left: 16, right: 16, bottom: 8),
child: Stack(
children: <Widget>[
Align(
alignment: Alignment.centerLeft,
child: Text(
'Kategori',
style: TextStyle(color: gray2, fontWeight: FontWeight.bold),
),
),
Align(
alignment: Alignment.centerRight,
child: GestureDetector(
child: Padding(
padding: const EdgeInsets.only(top: 8),
child: Text(
'See All',
style: TextStyle(
color: orange2, fontWeight: FontWeight.bold),
),
),
onTap: () {
Navigator.pushNamed(context, '/categories');
}),
)
],
),
),
StreamBuilder<List<Category>>(
stream: _categoryBloc.categoriesStream,
initialData: PageStorage.of(context).readState(context,
identifier: const ValueKey<String>('categories')),
builder:
(BuildContext context, AsyncSnapshot<List<Category>> snapshot) {
if (snapshot.hasData) {
PageStorage.of(context).writeState(context, snapshot.data,
identifier: const ValueKey<String>('categories'));
if (snapshot.data.isNotEmpty) {
return Container(
padding: const EdgeInsets.only(left: 16, right: 16, top: 8),
height: 100,
child: ListView.builder(
itemBuilder: (BuildContext context, int position) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
decoration: BoxDecoration(
border: Border.all(
width: 1,
color: gray1,
style: BorderStyle.solid),
borderRadius: const BorderRadius.all(
Radius.circular(8)),
color: gray3),
child: Padding(
padding: const EdgeInsets.all(8),
child: Image.network(
snapshot.data[position].image.thumb.url,
height: 40,
width: 40,
),
),
),
Padding(
padding: const EdgeInsets.only(top: 6),
child: Text(
snapshot.data[position].name,
style: TextStyle(fontSize: 11, color: black1),
),
),
],
);
},
scrollDirection: Axis.horizontal,
itemCount: snapshot.data.length,
),
);
} else {
return Text(
'Empty Data',
style: TextStyle(color: black1, fontSize: 11),
);
}
} else if (snapshot.hasError) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ToastTool(
context: context,
message: 'Failed to load categories')
.show();
});
}
return Progressbar.getProgressBar();
},
),
Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.only(left: 16, top: 8, right: 16),
child: Text(
'Terdekat',
style: TextStyle(color: gray2, fontWeight: FontWeight.bold),
),
),
StreamBuilder<List<Spot>>(
stream: _spotBloc.nearestSpotStream,
builder: (BuildContext context, AsyncSnapshot<List<Spot>> snapshot) {
print('data ${snapshot.error}');
if (snapshot.hasData) {
if (snapshot.data.isNotEmpty) {
return ListView.builder(
itemBuilder: (BuildContext context, int position) {
if (position >= snapshot.data.length) {
return Padding(
padding: const EdgeInsets.only(top: 12, bottom: 12),
child: Progressbar.getProgressBar(),
);
} else {
return GestureDetector(
child: Container(
padding: const EdgeInsets.all(12),
margin: EdgeInsets.only(
left: 16,
right: 16,
top: position != 0 ? 4 : 16,
bottom:
position != snapshot.data.length.toDouble()
? 4
: 16),
decoration: BoxDecoration(
border: Border.all(
color: gray1,
width: 0.5,
),
borderRadius:
const BorderRadius.all(Radius.circular(8.0))),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
snapshot.data[position].image.thumb.url !=
null
? Image.network(
snapshot
.data[position].image.thumb.url,
width: 48,
height: 48,
)
: Image.asset(
'assets/antrian_icon.png',
height: 48,
width: 48,
),
Padding(
padding: const EdgeInsets.only(left: 12),
child: Column(
children: <Widget>[
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Text(
snapshot.data[position].name !=
null
? '${snapshot.data[position].name}'
: 'Tidak Ada Nama',
style: const TextStyle(
fontWeight: FontWeight.bold),
),
Padding(
padding:
const EdgeInsets.only(top: 4),
child: Text(
snapshot.data[position]
.address !=
null
? '${snapshot.data[position].address}'
: 'Tidak Ada Alamat',
style: const TextStyle(
fontSize: 10, color: gray1),
),
)
],
)
],
),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
children: <Widget>[
Image.asset(
'assets/category_icon.png',
width: 24,
height: 24,
),
Text(
snapshot.data[position].queueCategory
.name !=
null
? '${snapshot.data[position].queueCategory.name}'
: 'Tidak Ada Katergori',
style: const TextStyle(fontSize: 10),
)
],
),
Padding(
padding: const EdgeInsets.only(left: 12),
child: Row(
children: <Widget>[
Icon(
Icons.access_time,
size: 18,
),
Text(
(snapshot.data[position].startTime !=
null &&
snapshot.data[position]
.endTime !=
null)
? '${snapshot.data[position].startTime} - ${snapshot.data[position].endTime}'
: 'Tidak Ada Jam',
style: const TextStyle(fontSize: 10),
)
],
),
)
],
)
],
),
),
onTap: () {
Navigator.of(context).pushNamed('/spot_page',
arguments: snapshot.data[position]);
},
);
}
},
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: snapshot.data.length < perPage
? snapshot.data.length
: snapshot.data.length + 1,
controller: _scrollController,
);
} else {
return Center(
child: const Text(
'Belum ada data',
style: TextStyle(fontSize: 12),
),
);
}
} else if (snapshot.error != null) {
return Center(
child: const Text(
'Gagal memuat data',
style: TextStyle(fontSize: 12),
),
);
}
return Padding(
padding: const EdgeInsets.only(top: 12, bottom: 12),
child: Progressbar.getProgressBar(),
);
},
)
],
)
]
),
)
],
),
);
}
}
Here is the best prorotype that you will get better idea about scrolling.这是最好的原型,您将更好地了解滚动。
Before it you should refer this link https://api.flutter.dev/flutter/widgets/ScrollController-class.html https://api.flutter.dev/flutter/widgets/ScrollNotification-class.html在此之前,您应该参考此链接https://api.flutter.dev/flutter/widgets/ScrollController-class.html https://api.flutter.dev/flutter/widgets/ScrollNotification-class.html
import 'package:flutter/material.dart';
main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ScrollNotificationPage(),
),
);
}
}
class ScrollNotificationPage extends StatefulWidget {
@override
_ScrollNotificationPageState createState() => _ScrollNotificationPageState();
}
class _ScrollNotificationPageState extends State<ScrollNotificationPage> {
String _progress = "0%";
ScrollController _controller = ScrollController();
@override
void initState() {
super.initState();
_controller.addListener(() {
debugPrint("_controller offset ${_controller.offset} position ${_controller.position}");
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("ScrollNotification"),
),
body: Scrollbar(
child: NotificationListener<ScrollNotification>(
// ignore: missing_return
onNotification: (ScrollNotification notification) {
double progress = notification.metrics.pixels / notification.metrics.maxScrollExtent;
setState(() {
_progress = "${(progress * 100).toInt()}%";
});
print("BottomEdge: ${notification.metrics.extentAfter == 0}");
},
child: Stack(
alignment: Alignment.center,
children: <Widget>[
ListView.builder(
controller: _controller,
itemCount: 100,
itemExtent: 50.0,
itemBuilder: (context, index) {
return ListTile(title: Text("$index"));
}),
CircleAvatar(
radius: 30.0,
child: Text(_progress),
backgroundColor: Colors.black54,
),
Positioned(
bottom: 20,
child: RaisedButton(
onPressed: () {
_controller.animateTo(0, duration: Duration(milliseconds: 500), curve: Curves.ease);
},
child: Text("Scroll Top"),
),
)
],
),
),
),
);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.