首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在可扩展列表中使用CircleAvatar时无法修复jank

在可扩展列表中使用CircleAvatar时无法修复jank
EN

Stack Overflow用户
提问于 2022-01-22 14:50:06
回答 1查看 45关注 0票数 0

我正在使用Ajay的expandable listview mentioned in this SO question。这很好,但是当我将它修改为包含FutureBuilder以加载CircleAvatar时,当我折叠列表时,它会导致jank。

  1. 当我单击向下箭头时,它最初会展开并显示person图标,因为图像没有加载。这很好,

  1. ,然后加载图像,将person图标替换为CircleAvatar中的实际配置文件图像。这也很好。
  2. ,但是当我单击向上箭头来折叠列表时,配置文件图像将被人图标替换为秒的分数,这将导致jank.

请看附图(很抱歉,分辨率差,使用一些在线转换器生成)。

我不明白为什么CircleAvatar会再次下载配置文件图像。为了让它记住状态,我尝试将它封装到一个StatefulCircleAvatar中,但这也不起作用。

这是我的代码:

代码语言:javascript
复制
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);
}
EN

回答 1

Stack Overflow用户

发布于 2022-01-22 15:20:28

UPD:将实际修复添加到所提问题中,但我仍然建议使用我在底部建议的技术。

特别是ListView和ListView.builder只呈现可见区域的小部件。若要保留AutomaticKeepAliveClientMixin.子级的状态,请使用ListView

您可以在这里试用以下代码:https://dartpad.dev/?id=7bef8a79f16bfb8259cf2204e3694822

代码语言:javascript
复制
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;

  1. 在init状态下为其赋值,以确保只调用一次:

代码语言:javascript
复制
  @override
  void initState() {
    super.initState();
    _myFutureUsers = getMyusers()
  }

  1. 用新变量

替换FutureBuilder中的未来

代码语言:javascript
复制
    FutureBuilder<List<String>>(
                          future: _myFutureUsers,
                          ...
    )
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70813979

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档