Flutter-RepaintBoundary导致StatefulWidget状态重置

时间:2019-05-23 11:21:55

标签: dart flutter statefulwidget

我有一个预览小部件,可在用户点击后加载数据。在滚动(预览位于列表中)或浏览其他屏幕时,不应丢失此状态(是否已点击)。 通过添加AutomaticKeepAliveClientMixin可以解决滚动问题,该功能可以在滚动时保存状态。

现在,我还需要使用RepaintBoundary包装预览小部件(实际上是包含预览的更复杂的小部件),以便能够单独对此小部件进行“截屏”。

在用RepaintBoundary包装小部件之前,在滚动和导航到另一个屏幕时都保存状态。 添加RepaintBoundary后,滚动仍然可以进行,但导航状态会重置。

如何包装带有RepaintBoundary的状态小部件?

代码是我实现相同问题的简化示例。

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {

  MyApp({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final title = 'Test';
    return MaterialApp(
      title: title,
      home: Scaffold(
        appBar: AppBar(
          title: Text(title),
        ),
        body: TestList(40),
      ),
    );
  }
}


class TestList extends StatefulWidget {

  final int numberOfItems;

  TestList(this.numberOfItems);

  @override
  _TestListState createState() => _TestListState();

}

class _TestListState extends State<TestList> {

  @override
  Widget build(BuildContext context) {
    print('_TestListState build.');
    return ListView.builder(
      itemCount: widget.numberOfItems,
      itemBuilder: (context, index) {

        return RepaintBoundary(
          key: GlobalKey(),
          child: Preview()
        );
      },
    );
  }
}


class Preview extends StatefulWidget {
  @override
  _PreviewState createState() => _PreviewState();
}

class _PreviewState extends State<Preview> with AutomaticKeepAliveClientMixin {

  bool loaded;

  @override
  void initState() {
    super.initState();
    print('_PreviewState initState.');

    loaded = false;
  }

  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context);

    print('_PreviewState build.');

    if(loaded) {
      return GestureDetector(
        onTap: () {
          Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => NewScreen()),
          );
        },
        child: ListTile(
          title: Text('Loaded. Tap to navigate.'),
          leading: Icon(Icons.visibility),
        ),
      );
    } else {
      return GestureDetector(
        onTap: () {
          setState(() {
            loaded = true;
          });
        },
        child: ListTile(
          title: Text('Tap to load.'),
        ),
      );
    }
  }
}


class NewScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('New Screen')),
      body: Center(
        child: Text(
          'Navigate back and see if loaded state is gone.',
          style: TextStyle(fontSize: 14.0),
        ),
      ),
    );
  }
}

1 个答案:

答案 0 :(得分:0)

看看RepaintBoundary.wrap,它会根据其子项或RepaintBoundarychildIndex小部件分配一个密钥,以便维持状态:

class _TestListState extends State<TestList> {
  @override
  Widget build(BuildContext context) {
    print('_TestListState build.');
    return ListView.builder(
      itemCount: widget.numberOfItems,
      itemBuilder: (context, index) {
        return RepaintBoundary.wrap(
          Preview(),
          index,
        );
      },
    );
  }
}

https://api.flutter.dev/flutter/widgets/RepaintBoundary/RepaintBoundary.wrap.html

编辑:根据以下评论,该解决方案似乎会破坏屏幕截图功能,因此您必须像这样存储子窗口小部件的状态:

class _TestListState extends State<TestList> {
  List<Widget> _children;

  @override
  void initState() {
    super.initState();
    _children = List.generate(
        widget.numberOfItems,
        (_) => RepaintBoundary(
              key: GlobalKey(),
              child: Preview(),
            ));
  }

  @override
  Widget build(BuildContext context) {
    print('_TestListState build.');
    return ListView(children: _children);
  }
}