Flutter - RepaintBoundary causes state reset of StatefulWidget

风格不统一 提交于 2020-01-23 16:57:48

问题


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.

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.

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

回答1:


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

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

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


来源:https://stackoverflow.com/questions/56274124/flutter-repaintboundary-causes-state-reset-of-statefulwidget

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!