我正在使用Ajay的expandable listview mentioned in this SO question。这很好,但是当我将它修改为包含FutureBuilder以加载CircleAvatar时,当我折叠列表时,它会导致jank。
,
。
请看附图(很抱歉,分辨率差,使用一些在线转换器生成)。

我不明白为什么CircleAvatar会再次下载配置文件图像。为了让它记住状态,我尝试将它封装到一个StatefulCircleAvatar中,但这也不起作用。
这是我的代码:
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: Home()));
}
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(
title: Text("Expandable List"),
backgroundColor: Colors.redAccent,
),
body: ListView.builder(
itemBuilder: (BuildContext context, int index) {
return ExpandableListView(title: "Title $index");
},
itemCount: 5,
),
);
}
}
class ExpandableListView extends StatefulWidget {
final String title;
const ExpandableListView({Key? key, required this.title}) : super(key: key);
@override
_ExpandableListViewState createState() => _ExpandableListViewState();
}
class _ExpandableListViewState extends State<ExpandableListView> {
bool expandFlag = false;
List<String>? _cachedUsers;
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(vertical: 1.0),
child: Column(
children: <Widget>[
Container(
color: Colors.blue,
padding: EdgeInsets.symmetric(horizontal: 5.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: Container(
height: 50.0,
width: 50.0,
decoration: const BoxDecoration(
color: Colors.orange,
shape: BoxShape.circle,
),
child: Center(
child: Icon(
expandFlag ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down,
color: Colors.white,
size: 20.0,
),
),
),
onPressed: () {
setState(() {
expandFlag = !expandFlag;
if(expandFlag) _cachedUsers = null;
});
}),
Text(
widget.title,
style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
)
],
),
),
ExpandableContainer(
expanded: expandFlag,
child:
(expandFlag && _cachedUsers == null) ?
FutureBuilder<List<String>>(
future: getMyusers(),
builder: (context, snapshot) {
if (!snapshot.hasData ||
snapshot.connectionState == ConnectionState.waiting) {
return Container(
height: 200,
width: 200,
child: Center(child: CircularProgressIndicator()));
}
if (snapshot.hasError) {
return Container(
height: 200,
width: 200,
child: Center(child: Text(snapshot.error.toString())));
}
_cachedUsers = snapshot.data!;
return ListView.builder(
controller: ScrollController(),
itemCount: _cachedUsers!.length,
itemBuilder: (context, index) {
return Container(
decoration:
BoxDecoration(border: Border.all(width: 1.0, color: Colors.white), color: Colors.black),
child: ListTile(
title: Text(
_cachedUsers![index],
style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
),
leading: StatefulCircleAvatar(userid:10),
),
);
},
);
},
)
:
ListView.builder(
controller: ScrollController(),
itemCount:
_cachedUsers==null?0:_cachedUsers!.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(
_cachedUsers![index],
style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
),
leading:// getCircleAvatar(24,NetworkImage("https://lh3.googleusercontent.com/a/AATXAJxaVweFMPZXdbMuuwNAkqwdbw15IK75dYGjRHp6=s96-c"), Colors.amber,),
StatefulCircleAvatar(userid:10),
);
},
)
)
],
),
);
}
Future<List<String>> getMyusers() async{
await Future.delayed(Duration(seconds: 1 ));
return [for(int i=0; i<10; i++) 'user $i'];
}
}
class ExpandableContainer extends StatelessWidget {
final bool expanded;
final double collapsedHeight;
final double expandedHeight;
final Widget child;
ExpandableContainer({
required this.child,
this.collapsedHeight = 0.0,
this.expandedHeight = 300.0,
this.expanded = true,
});
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
return AnimatedContainer(
duration: Duration(milliseconds: 500),
curve: Curves.easeInOut,
width: screenWidth,
height: expanded ? expandedHeight : collapsedHeight,
child: Container(
child: child,
decoration: BoxDecoration(border: Border.all(width: 1.0, color: Colors.blue)),
),
);
}
}
class StatefulCircleAvatar extends StatefulWidget {
int userid;
StatefulCircleAvatar({required this.userid});
@override
_StatefulCircleAvatarState createState() => _StatefulCircleAvatarState();
}
class _StatefulCircleAvatarState extends State<StatefulCircleAvatar> with AutomaticKeepAliveClientMixin<StatefulCircleAvatar>{
bool get wantKeepAlive => true;
bool _checkLoading = true;
NetworkImage? myImage;
@override
void initState() {
getProfilepicUrl(widget.userid).then((value) {
myImage = NetworkImage(value);
myImage!.resolve(ImageConfiguration()).addListener(ImageStreamListener ( (_, __) {
if (mounted) {
setState(() {
_checkLoading = false;
});
}
}) as ImageStreamListener);
});
}
Widget? ca;
@override
Widget build(BuildContext context) {
print('sfca build called $_checkLoading');
return _checkLoading == true ? CircleAvatar(
child: personIcon) : CircleAvatar(
backgroundImage: myImage,); }
Future<String> getProfilepicUrl(int userid) async{
await Future.delayed(Duration(seconds: 1 ));
print('getprofilepicurl called');
return "https://lh3.googleusercontent.com/a/AATXAJxaVweFMPZXdbMuuwNAkqwdbw15IK75dYGjRHp6=s96-c";
}
Icon personIcon = const Icon(Icons.person);
}发布于 2022-01-22 15:20:28
UPD:将实际修复添加到所提问题中,但我仍然建议使用我在底部建议的技术。
特别是ListView和ListView.builder只呈现可见区域的小部件。若要保留AutomaticKeepAliveClientMixin.子级的状态,请使用ListView
您可以在这里试用以下代码:https://dartpad.dev/?id=7bef8a79f16bfb8259cf2204e3694822
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: ListView.builder(
itemBuilder: (context, index) => MyStatefulWidget(index: index),
itemCount: 10,
),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({required this.index, Key? key}) : super(key: key);
final int index;
@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> with AutomaticKeepAliveClientMixin {
@override
void initState() {
super.initState();
// Will only be called once, because the widget
// saves its state even when not visible in ListView
print("init state called at: ${widget.index}");
}
@override
Widget build(BuildContext context) {
super.build(context);
return Container(
height: 400,
child: Text("My Statful Widget ${widget.index + 1}"),
);
}
@override
bool get wantKeepAlive => true;
}旧答案如下:
这可能是因为您的FutureBuilder在状态更改时多次调用函数getMyusers()。你能做的就是:
在_ExpandableListViewState:中创建一个新变量
late Future<List<String>> _myFutureUsers;
@override
void initState() {
super.initState();
_myFutureUsers = getMyusers()
}替换FutureBuilder中的未来
FutureBuilder<List<String>>(
future: _myFutureUsers,
...
)https://stackoverflow.com/questions/70813979
复制相似问题