paintBoundary导致StatefulWidget的状态

paintBoundary导致StatefulWidget的状态

本文介绍了Flutter-RepaintBoundary导致StatefulWidget的状态重置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a preview widget that loads data after a user tap. This state (already tapped or not) should not be lost while scrolling (the preview is located in a list) or navigating through other screen.The scrolling is solved by adding AutomaticKeepAliveClientMixin which saves the state when scrolling away.

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

Now i also need to wrap the preview widget (actually a more complex widget that contains the preview) with a RepaintBoundary, to be able to make a "screenshot" of this widget alone.

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

Before i wrap the widget with a RepaintBoundary, the state is saved both while scrolling and navigating to another screen.After i add the RepaintBoundary the scrolling still works but for navigation the state is reset.

我如何包装一个有状态的小部件,该小部件应该使用重绘边界?

How can i wrap a Stateful widget that should hold its state with a RepaintBoundary?

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

Code is a simplified example of my implementation with the same problem.

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),
        ),
      ),
    );
  }
}


推荐答案

看看 RepaintBoundary.wrap ,它会根据其子项或 RepaintBoundary 小部件分配一个键。 > childIndex ,因此维持状态:

Take a look at RepaintBoundary.wrap, it assigns the RepaintBoundary widget a key based on its child or childIndex so state is maintained:

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,
        );
      },
    );
  }
}

编辑:根据以下注释,此解决方案似乎破坏了屏幕截图功能,因此您必须存储子级小部件列表

As per the below comments, it looks like this solution would break the screenshot ability so you'd have to store the list of children widgets in your state like so:

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);
  }
}

这篇关于Flutter-RepaintBoundary导致StatefulWidget的状态重置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-01 16:45