【发布时间】:2021-10-20 19:28:25
【问题描述】:
我有一个带有标题文本的列和一个嵌套的 StreamBuilder,我通过它构建了一个自定义项列表,这些自定义项是通过我的 BloC 从 Firebase 接收的。但是我注意到,如果我向下滚动,然后尝试缓慢或快速地向上滚动,滚动似乎会非常快速地摆动(向上/向下)并且向上移动的进展非常小。
我在 Profile 模式下运行该应用程序并见证了着色器垃圾(我读到可以通过预热 Skia 着色器来修复)和其他一些延迟,但在我描述的问题期间没有。一切都在 16 毫秒以下。 grouped_list 库似乎也没有任何活跃的相关问题,所以我不确定它是否就是这样。这是我页面的代码和一个视频,可以更好地描述这个问题:
class PickUpPage extends StatefulWidget {
const PickUpPage({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => PickUpPageState();
}
class PickUpPageState extends State<PickUpPage> {
late PickUpScreenBloc _bloc;
late GameDetailsBloc _detailsBloc;
late final StreamSubscription _idsStreamSub;
@override
void initState() {
super.initState();
_bloc = PickUpScreenBloc();
_detailsBloc = GameDetailsBloc();
_setListeners();
}
void _setListeners() {
_idsStreamSub = _bloc.idsStream.listen((ids) {
_detailsBloc.getDetailsUsingIds(ids);
});
}
@override
void dispose() {
_idsStreamSub.cancel();
_bloc.dispose();
_detailsBloc.dispose();
super.dispose();
}
@override
void deactivate() {
_bloc.dispose();
_detailsBloc.dispose();
super.deactivate();
}
// used to rebuild the page when a user logged-in and returned to the list page
FutureOr _onNavigateBack(dynamic val) {
setState(() {});
}
void _handleGameSelected(PickUpGameDetails details) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => GameDetailsPage(
details: details,
),
),
).then((value) => _onNavigateBack(value));
}
@override
Widget build(BuildContext context) {
User? user = FirebaseAuth.instance.currentUser;
return Column(
children: [
Container(
alignment: Alignment.centerLeft,
padding:
const EdgeInsets.only(top: 15, left: 15, right: 15, bottom: 15),
child: user == null
? const Text(
'Choose a pick-up game to play in:',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
)
: Text(
'Hey ${user.displayName == null ? '{no display name}' : user.displayName!}, choose a pick-up game to play in:',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
),
const SizedBox(
height: 10,
),
BlocProvider.value(
value: _detailsBloc,
child: StreamBuilder<PickUpGameDetails>(
stream: _detailsBloc.gameDetailsStream,
builder: (context, snapshot) {
if (!snapshot.hasError) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
} else {
return Expanded(
child: GroupedListView<PickUpGameDetails, String>(
elements: _detailsBloc.gameDetailsList,
sort: true,
order: GroupedListOrder.ASC,
groupComparator: (group1, group2) =>
group1.compareTo(group2),
groupBy: (gameItem) =>
gameItem.gameData!.dateTime!.substring(4, 8),
itemComparator: (item1, item2) =>
GameData.getGame24hTime(item1.gameData!.dateTime!)
.compareTo(GameData.getGame24hTime(
item2.gameData!.dateTime!)),
indexedItemBuilder: (BuildContext context,
PickUpGameDetails details, int index) =>
InkWell(
splashColor: const Color(0xffff5a5f),
child: PickUpGameItem(
details.gameId!, details, Key(index.toString())),
onTap: () => {_handleGameSelected(details)},
),
groupHeaderBuilder: (PickUpGameDetails details) =>
Padding(
padding: const EdgeInsets.only(
left: 20, top: 5, bottom: 5),
child: Text(
details.gameData!.formattedDateTime,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Colors.black),
),
),
),
);
}
} else {
return ErrorWidget('Something went wrong!');
}
}),
),
],
);
}
}
class PickUpGameItem extends StatefulWidget {
final String gameId;
final PickUpGameDetails details;
const PickUpGameItem(this.gameId, this.details, Key? key) : super(key: key);
@override
_PickUpGameItemState createState() => _PickUpGameItemState();
}
class _PickUpGameItemState extends State<PickUpGameItem> {
PickUpGameDetails? _gameDetails;
GameDetailsBloc? _detailsBloc;
@override
void initState() {
super.initState();
_detailsBloc = BlocProvider.of<GameDetailsBloc>(context);
_detailsBloc!.subscribeToGameDetailsUpdatesWithId(widget.gameId);
}
@override
Widget build(BuildContext context) {
return StreamBuilder<Tuple2<String, PickUpGameDetails>>(
stream: _detailsBloc!.detailsUpdatesStream,
builder: (context, snapshot) {
if (snapshot.hasError ||
snapshot.data == null ||
snapshot.connectionState == ConnectionState.waiting) {
_gameDetails = widget.details;
} else {
if (snapshot.data!.item1 == widget.gameId) {
_gameDetails = snapshot.data!.item2;
} else {
_gameDetails = widget.details;
}
}
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(15.0),
child: _gameDetails!.locationInfo == null
? const SizedBox()
: CachedNetworkImage(
imageUrl:
_gameDetails!.locationInfo!.pictures.elementAt(0),
width: 80,
height: 80,
fit: BoxFit.fill,
placeholder: (context, url) => const SizedBox(
child: Center(child: CircularProgressIndicator()),
width: 10,
height: 10),
),
),
const SizedBox(
width: 10,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_gameDetails!.locationInfo == null
? 'Loading...'
: _gameDetails!.locationInfo!.nam,
style: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 16),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(
height: 10,
),
_gameDetails!.gameData!.hostInfo == null
? Text(
_gameDetails!.gameData!.gameTypeMsg!,
style: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.normal,
fontSize: 14),
maxLines: 2,
overflow: TextOverflow.ellipsis,
)
: Text(
'${_gameDetails!.gameData!.gameTypeMsg!} with ${_gameDetails!.gameData!.hostInfo!.hostNickname}.',
style: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.normal,
fontSize: 14),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
),
const SizedBox(
width: 5,
),
Flexible(
flex: 0,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
GameData.getGameTimestamp(
_gameDetails!.gameData!.dateTime!),
style:
const TextStyle(color: Colors.black, fontSize: 15),
),
const SizedBox(
height: 10,
),
Row(
children: [
Text(
'${_gameDetails!.gameData!.getCurrentPlayerNumber()}/${_gameDetails!.gameData!.maxPlayers}',
style: const TextStyle(
color: Colors.grey,
fontWeight: FontWeight.normal,
fontSize: 14),
),
const SizedBox(
width: 5,
),
const ImageIcon(
AssetImage('assets/icons/profile.png'))
],
),
],
),
),
],
),
);
});
}
}
【问题讨论】:
标签: flutter dart flutter-listview