滚动侦听器不适用于 SingleChildScrollView Flutter 中的列表视图
Posted
技术标签:
【中文标题】滚动侦听器不适用于 SingleChildScrollView Flutter 中的列表视图【英文标题】:Scroll listener doesn't work for listview inside a SingleChildScrollView Flutter 【发布时间】:2020-03-25 15:59:34 【问题描述】:我目前正在做一个项目,该项目需要一个列表视图作为 ScrollView 的子视图。我在列表视图中添加了一个滚动控制器,并在滚动控制器中添加了一个滚动监听器。但是监听器方法永远不会被调用。如果我在父滚动视图中添加控制器,就会调用它。我需要listview的监听器,因为我必须在滚动时通过api调用分页数据。 这是我的代码:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_calendar_carousel/classes/event.dart';
import 'package:flutter_calendar_carousel/flutter_calendar_carousel.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:http/http.dart' as http;
import 'package:hub_food_park/network/network_helper.dart';
import 'package:hub_food_park/pojos/event/event_item.dart';
import 'package:hub_food_park/pojos/event/event_result.dart';
import 'package:hub_food_park/utils/constants.dart';
import 'package:intl/intl.dart';
class FirstScreen extends StatefulWidget
@override
State<StatefulWidget> createState()
// TODO: implement createState
return _FirstScreeState();
class _FirstScreeState extends State<FirstScreen>
DateTime _currentDate = DateTime.now();
bool isEmpty = false;
String _currentMonth = '';
String monthHeader = '';
NetworkHelper mHelper;
int listSize = 0;
List<Events> eventList = List<Events>();
Future<List<Events>> future;
CalendarCarousel _calendarCarousel;
String selectedDate = '';
String _currentYear = '';
bool isMonthly = false;
Color color;
bool isLoading = false;
int pageNo = 0;
ScrollController _listScrollController;
String message;
List<String> monthNames = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
];
final GlobalKey widgetKey = GlobalKey();
void _changed(bool visibility)
setState(()
isEmpty = visibility;
);
String _getFormattedDate(DateTime dateTime)
var formatter = new DateFormat('yyyy-MM-dd');
String formattedDate = formatter.format(dateTime);
return formattedDate;
@override
void initState()
_listScrollController = ScrollController();
_listScrollController.addListener(_scrollListener);
super.initState();
var d = DateTime.now();
DateTime dateTime = DateTime(d.year, d.month, d.day);
selectedDate = _getFormattedDate(dateTime);
_currentMonth = selectedDate.substring(5, 7);
_currentYear = selectedDate.substring(0, 4);
print(_currentMonth + ' ' + _currentYear);
mHelper = new NetworkHelper('events');
// getList(selectedDate, pageNo);
future = _getTodaysEvent(selectedDate);
void getList(String selectedDate, int pageNo)
mHelper.setUrl('events');
mHelper.getEventList(selectedDate, pageNo).then((v)
setState(()
if (v.events != null)
print(v.events.length);
listSize = v.events.length;
eventList.addAll(v.events);
_changed(false);
else
print(v.message);
if (pageNo == 0)
_changed(true);
pageNo = 0;
);
);
void getMonthlyEventList(int month, int year, int pageNo)
mHelper.setUrl('monthly');
mHelper.getMonthlyEventList(month, year, pageNo).then((v)
setState(()
if (v.events != null)
print(v.events.length);
listSize = v.events.length;
eventList.clear();
eventList.addAll(v.events);
_changed(false);
else
print(v.message);
_changed(true);
);
);
@override
void dispose()
_listScrollController.dispose();
super.dispose();
_scrollListener()
print("got here");
if (_listScrollController.position.extentAfter <= 0 && isLoading == false)
_getTodaysEvent(selectedDate);
@override
Widget build(BuildContext context)
final listView = ListView.builder(
controller: _listScrollController,
physics: AlwaysScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: _buildItemsForListView,
itemCount: eventList.length,
);
monthHeader = monthNames[DateTime.now().month - 1] +
' ' +
DateTime.now().year.toString();
_calendarCarousel = CalendarCarousel<Event>(
todayBorderColor: kbottomContainerColor,
onDayPressed: (DateTime date, List<Event> events)
this.setState(() => _currentDate = date);
events.forEach((event) => print(event.title));
selectedDate = _getFormattedDate(_currentDate);
eventList.clear();
pageNo = 0;
_getTodaysEvent(selectedDate);
isMonthly = false;
print(_currentDate);
,
/* rightButtonIcon: FlatButton.icon(
onPressed: ()
setState(()
_currentDate = _currentDate.add(Duration(days: 30));
_currentMonth = DateFormat.yMMM().format(_currentDate);
monthHeader = _currentMonth;
);
,
icon: Icon(
Icons.chevron_right,
color: kbottomContainerColor,
),
label: Text(""),
),
leftButtonIcon: FlatButton.icon(
onPressed: ()
setState(()
_currentDate = _currentDate.subtract(Duration(days: 30));
_currentMonth = DateFormat.yMMM().format(_currentDate);
monthHeader = _currentMonth;
);
,
icon: Icon(
Icons.chevron_left,
color: kbottomContainerColor,
),
label: Text(""),
),*/
daysHaveCircularBorder: null,
showOnlyCurrentMonthDate: false,
weekendTextStyle: TextStyle(
color: Colors.white,
),
thisMonthDayBorderColor: Colors.grey,
weekFormat: false,
// firstDayOfWeek: 4,
height: 350.0,
selectedDateTime: _currentDate,
customGridViewPhysics: NeverScrollableScrollPhysics(),
markedDateCustomShapeBorder:
CircleBorder(side: BorderSide(color: Color(0xFFCA3F43))),
markedDateCustomTextStyle: TextStyle(
fontSize: 16.0,
color: Color(0xFFCA3F43),
),
showHeader: true,
headerText: monthHeader,
headerTextStyle: TextStyle(
color: Colors.white, fontSize: 16.0, fontStyle: FontStyle.normal),
isScrollable: false,
todayTextStyle: TextStyle(
color: Colors.white,
),
todayButtonColor: Colors.pinkAccent,
selectedDayTextStyle: TextStyle(
color: Colors.white,
),
weekdayTextStyle: TextStyle(color: Colors.white),
selectedDayButtonColor: Color(0xFFCA3F43),
selectedDayBorderColor: Colors.blue,
daysTextStyle: TextStyle(
color: Colors.white,
),
minSelectedDate: _currentDate.subtract(Duration(days: 360)),
maxSelectedDate: _currentDate.add(Duration(days: 360)),
prevDaysTextStyle: TextStyle(
fontSize: 16,
color: Colors.white70,
),
nextDaysTextStyle: TextStyle(
fontSize: 16,
color: Colors.white70,
),
inactiveDaysTextStyle: TextStyle(
color: Colors.tealAccent,
fontSize: 16,
),
onCalendarChanged: (DateTime date)
this.setState(() => _currentMonth = DateFormat.yMMM().format(date));
monthHeader = _currentMonth;
,
onDayLongPressed: (DateTime date)
print('long pressed date $date');
,
);
// TODO: implement build
return SafeArea(
child: Scaffold(
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
//custom icon
Padding(
padding: const EdgeInsets.all(4.0),
child: Container(
width: double.infinity,
color: kbottomContainerColor,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: _calendarCarousel,
),
),
),
SizedBox(
height: 8.0,
),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Expanded(
child: GestureDetector(
onTap: ()
setState(()
isMonthly = false;
pageNo = 0;
eventList.clear();
// getList(selectedDate, pageNo);
_getTodaysEvent(selectedDate);
);
,
child: Container(
height: 30.0,
color: kbottomContainerColor,
child: Center(
child: Text(
"Todays",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
),
),
),
),
),
),
Expanded(
child: GestureDetector(
onTap: ()
setState(()
isMonthly = true;
pageNo = 0;
getMonthlyEventList(int.parse(_currentMonth),
int.parse(_currentYear), pageNo);
);
,
child: Container(
height: 30.0,
color: Colors.white,
child: Center(
child: Text(
"Monthly",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.black,
fontSize: 16.0,
),
),
),
),
),
),
]),
Container(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: isEmpty
? Card(
color: Colors.white,
child: Column(
children: <Widget>[
Padding(
padding:
const EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 0),
child: Icon(
FontAwesomeIcons.sadTear,
color: kbottomContainerColor,
size: 40.0,
),
),
SizedBox(
height: 16.0,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Sorry we do not have any event on this date',
style: TextStyle(
color: Colors.grey.shade800,
fontSize: 16.0,
),
),
),
],
),
)
: FutureBuilder<List<Events>>(
builder:
(BuildContext context, AsyncSnapshot snapshot)
switch (snapshot.connectionState)
case ConnectionState.none:
case ConnectionState.active:
case ConnectionState.waiting:
return CircularProgressIndicator();
case ConnectionState.done:
if (snapshot.data != null)
isLoading = false;
return listView;
break;
,
future: future,
),
),
)
],
),
)),
);
Widget _buildItemsForListView(BuildContext context, int index)
if (isLoading)
return CircularProgressIndicator(
backgroundColor: kbottomContainerColor,
);
else
return Card(
elevation: 4.0,
color: Colors.white,
margin: EdgeInsets.symmetric(
vertical: 8.0,
horizontal: 8.0,
),
child: Container(
child: ListTile(
contentPadding:
EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0),
leading: Container(
child: Text(
'$index + 1',
style: TextStyle(
fontSize: 16.0,
color: Colors.grey.shade800,
),
),
),
title: Text(
eventList[index].title,
style:
TextStyle(color: Colors.black, fontWeight: FontWeight.bold),
),
// subtitle: Text("Intermediate", style: TextStyle(color: Colors.white)),
subtitle: Row(
children: <Widget>[
Flexible(
child: RichText(
overflow: TextOverflow.ellipsis,
strutStyle: StrutStyle(fontSize: 12.0),
text: TextSpan(
style: TextStyle(color: Colors.grey.shade800),
text: eventList[index].details,
),
),
),
],
),
trailing: Text(
eventList[index].startTime,
style: TextStyle(
fontSize: 12.0,
color: Colors.grey.shade800,
),
),
),
),
);
Widget _buildProgressIndicator()
return new Padding(
padding: const EdgeInsets.all(8.0),
child: new Center(
child: new Opacity(
opacity: isLoading ? 1.0 : 00,
child: new CircularProgressIndicator(),
),
),
);
Future<List<Events>> _getTodaysEvent(String date) async
isLoading = true;
String mUrl = BASE_URL + 'events?date=$date&page=$pageNo';
print(mUrl);
try
http.Response response = await http.get(mUrl);
print(response.statusCode);
Map data = jsonDecode(response.body);
if (data.containsKey('status'))
return null;
else
EventResult result = EventResult.fromJson(data);
eventList.addAll(result.events);
setState(()
pageNo++;
);
return eventList;
catch (e)
print(e);
return null;
【问题讨论】:
【参考方案1】:使用CustomScrollView 获得您想要的设计
【讨论】:
以上是关于滚动侦听器不适用于 SingleChildScrollView Flutter 中的列表视图的主要内容,如果未能解决你的问题,请参考以下文章
位置侦听器适用于 Android 6.0 但不适用于 android 8.0+ [关闭]