简体   繁体   English

调用 SetState() 时,如何避免带有 UniqueKey 的 Flutter FutureBuilder 闪烁?

[英]How to avoid Flutter FutureBuilder with UniqueKey flickering when SetState() is called?

I have a PageView (parent PageView) and each of its pages is a StatefulWidget that returns FutureBuilder .我有一个PageView (父 PageView),它的每个页面都是一个返回FutureBuilderStatefulWidget The FutureBuilder itself will return a PageView (child PageView), too. FutureBuilder本身也会返回一个PageView (子 PageView)。 Because I want both parent and child PageView to keep page, so both parent and child widget are implemented with AutomaticKeepAliveClientMixin .因为我希望父级和子PageView都保留页面,所以父级和子级小部件都使用AutomaticKeepAliveClientMixin实现。

Each page is assigned a UniqueKey because the pages are dynamic, meaning users can delete some pages or add pages.每个页面都分配了一个UniqueKey ,因为页面是动态的,这意味着用户可以删除某些页面或添加页面。 However when SetState() is called, there're two problems: 1 the whole page flickers;但是在调用SetState()时,有两个问题: 1 整个页面闪烁; 2 the child PageView jumps back to page at index 0. 2 子PageView跳回到索引 0 处的页面。

If I don't use UniqueKey , these two issues disappear.如果我不使用UniqueKey ,这两个问题就会消失。 But then after adding or deleting pages, you can't refresh the pages.但是添加或删除页面后,您无法刷新页面。

Any advice are appreciated, thanks!任何建议表示赞赏,谢谢!

Codes that shows the Flickering:显示闪烁的代码:

void main() async {
  runApp(
    MaterialApp(
      title: 'Test',
      home: Scaffold(
        body: Bar(),
      ),
    ),
  );
}

class Bar extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _BarState();
}

class _BarState extends State<Bar> {
  List<Widget> pages;

  final controller = PageController(initialPage: 0, keepPage: true);
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    pages = [
      Center(child: PageWithFutureBuilder(key: UniqueKey())),
      Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          PageWithFutureBuilder(key: UniqueKey()),
          RaisedButton(
            onPressed: () {
              refresh();
            },
            child: new Icon(
              Icons.refresh,
              color: Colors.blue,
              size: 30.0,
            ),
          )
        ],
      ),
    ];
    return PageView(
      controller: controller,
      children: pages,
    );
  }

  void refresh() async {
    setState(() {});
  }
}

class PageWithFutureBuilder extends StatefulWidget {
  PageWithFutureBuilder({Key key}) : super(key: key);
  @override
  State<StatefulWidget> createState() => _PageWithFutureBuilderState();
}

Future test() async {
  return;
}

class _PageWithFutureBuilderState extends State<PageWithFutureBuilder> {
  Future future;
  @override
  void initState() {
    future = test();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: future,
      builder: (ocntext, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting)
          return Container(
            color: Colors.black,
            child: Center(child: CircularProgressIndicator()),
          );
        else if (snapshot.connectionState == ConnectionState.done) {
          return Center(child: Text('test text'));
        } else
          return Container();
      },
    );
  }
}

Swipe to the 2nd page, then click on the button, you'll see the flickering.滑动到第二页,然后单击按钮,您会看到闪烁。 As to the codes of jumping back to page at index 0 , they're too complex to put here, and it's probably the same reason with the flickering.至于jumping back to page at index 0的代码,太复杂了,就不放了,估计和闪烁的原因是一样的。

I think I've found the right solution.我想我找到了正确的解决方案。 Basically it's caused by using a wrong Key .基本上是用错了Key造成的。 Using a UniqueKey makes Flutter regarding the child widget as different so it re-initialized new child widget every time it rebuilds.使用UniqueKey使 Flutter 将子小部件视为不同,因此每次重建时它都会重新初始化新的子小部件。 Hence the flickering and jumping back to page at index 0.因此闪烁并跳回索引 0 处的页面。

Instead of using a UniqueKey , use a value key.不要使用UniqueKey ,而是使用值键。 In my case each of my child parent has a unique object ID , so I use Key(objectID) as the key.在我的例子中,我的每个孩子父母都有一个唯一的object ID ,所以我使用Key(objectID)作为键。 So when SetState() get called, flutter will know some widgets can be reused since they have the same key, so the flickering disappears and it won't jump back to page at index 0.因此,当调用SetState()时,flutter 将知道某些小部件可以重复使用,因为它们具有相同的键,因此闪烁消失并且它不会跳回到索引 0 处的页面。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM