【问题标题】:Widgets in Gridview lose state when off-screenGridview 中的小部件在离屏时丢失状态
【发布时间】:2017-10-25 10:17:45
【问题描述】:

我刚开始玩 Flutter/Dart,我想知道为什么我的 Card 小部件在滚动到屏幕外时会失去其状态。

_isSelected 由用户点击其中一个Card 小部件来切换。一切都很好,直到它们从屏幕上消失——此时它们恢复正常。我假设我必须采取额外的步骤来保持状态,但我不太确定如何最好地解决这个问题。

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class CardHolder extends StatefulWidget {
  CardHolder({Key key}) : super(key: key);

  @override
  _CardHolderState createState() => new _CardHolderState();
}

class _CardHolderState extends State<CardHolder> {
  List _cardData;

  _getCards() async {
    String endpoint = 'https://jsonplaceholder.typicode.com/posts';
    var httpClient = createHttpClient();
    var response = await httpClient.read(endpoint);

    List data = JSON.decode(response);

    if (!mounted) return;

    setState(() {
      _cardData = data;
    });
  }

  @override
  void initState() {
    super.initState();

    _getCards();
  }

  @override
  Widget build(BuildContext context) {
    return new GridView.extent(
        maxCrossAxisExtent: 250.0,
        mainAxisSpacing: 4.0,
        crossAxisSpacing: 4.0,
        children: _buildGridList(_cardData)
      );
  }
}

List<Card> _buildGridList(data) {
  if (data == null) return [];
  List<Card> cards = [];
  for (var card in data) {
    cards.add(new Card(title: card['title']));
  }
  return cards;
}

class Card extends StatefulWidget {
  Card({Key key, this.title}) : super(key: key);

  final String title;

  @override
  CardState createState() => new CardState();
}

class CardState extends State<Card> {

  String title;
  bool _isSelected = false;

  _toggleSelected() {
    setState(() {
      _isSelected = !_isSelected;
      print('Toggled to ' + _isSelected.toString());
    });
  }

  CardState({this.title = "No Title!"});

  @override
  Widget build(BuildContext context) {
    print('Rendering card: ' + widget.title);
    return new GestureDetector(
        onTap: _toggleSelected,
        child: new Container(
            constraints: new BoxConstraints(minHeight: 120.0, minWidth: 100.0, maxWidth: 100.0),
            decoration: new BoxDecoration(
                color: _isSelected ? Colors.red : Colors.white,
                borderRadius: new BorderRadius.all(new Radius.circular(2.5)),
                boxShadow: [new BoxShadow(color: Colors.black45, blurRadius: 5.0, spreadRadius: 0.0, offset: new Offset(0.0, 3.0))]
            ),
            margin: new EdgeInsets.all(5.0),
            padding: new EdgeInsets.all(10.0),
            child: new Text(widget.title, style: new TextStyle(color: Colors.black))
        )
    );
  }
}

【问题讨论】:

    标签: android gridview dart flutter


    【解决方案1】:

    在 Flutter 旨在清理您的屏幕外State 的意义上,此行为按预期工作。您可以通过在树的更高级别维护 isSelected 布尔值来获得所需的行为,例如在CardHolder 或模型类中。对于简单的情况,ValueNotifier 可能就足够了。这是一个例子。

    import 'dart:collection';
    import 'package:flutter/scheduler.dart';
    import 'package:flutter/material.dart';
    import 'dart:convert';
    
    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    import 'package:flutter/foundation.dart';
    
    void main() {
      runApp(new MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'Flutter Demo',
          theme: new ThemeData(
            primarySwatch: Colors.blue,
            primaryColorBrightness: Brightness.light,
          ),
          home: new CardHolder(),
        );
      }
    }
    
    class CardHolder extends StatefulWidget {
      CardHolder({Key key}) : super(key: key);
    
      @override
      _CardHolderState createState() => new _CardHolderState();
    }
    
    class _CardHolderState extends State<CardHolder> {
      List _cardData;
    
      _getCards() async {
        String endpoint = 'https://jsonplaceholder.typicode.com/posts';
        var httpClient = createHttpClient();
        var response = await httpClient.read(endpoint);
    
        List data = JSON.decode(response);
    
        if (!mounted) return;
    
        setState(() {
          _cardData = data;
        });
      }
    
      @override
      void initState() {
        super.initState();
    
        _getCards();
      }
    
      @override
      Widget build(BuildContext context) {
        return new GridView.extent(
          maxCrossAxisExtent: 250.0,
          mainAxisSpacing: 4.0,
          crossAxisSpacing: 4.0,
          children: _buildGridList(_cardData)
        );
      }
    }
    
    List<Card> _buildGridList(data) {
      if (data == null) return [];
      List<Card> cards = [];
      for (var card in data) {
        cards.add(new Card(
          title: card['title'],
          isSelected: new ValueNotifier<bool>(false),
        ));
      }
      return cards;
    }
    
    class Card extends AnimatedWidget {
      Card({Key key, this.title, this.isSelected }) : super(key: key, listenable: isSelected);
    
      final String title;
      final ValueNotifier<bool> isSelected;
    
      @override
      Widget build(BuildContext context) {
        print('Rendering card: ' + title);
        return new GestureDetector(
          onTap: () {
            isSelected.value = !isSelected.value;
          },
          child: new Container(
            constraints: new BoxConstraints(minHeight: 120.0, minWidth: 100.0, maxWidth: 100.0),
            decoration: new BoxDecoration(
              color: isSelected.value ? Colors.red : Colors.white,
              borderRadius: new BorderRadius.all(new Radius.circular(2.5)),
              boxShadow: [new BoxShadow(color: Colors.black45, blurRadius: 5.0, spreadRadius: 0.0, offset: new Offset(0.0, 3.0))]
            ),
            margin: new EdgeInsets.all(5.0),
            padding: new EdgeInsets.all(10.0),
            child: new Text(title, style: new TextStyle(color: Colors.black))
          )
        );
      }
    }
    

    【讨论】:

    • 我认为这与它有关。快速跟进:为什么将 Card 小部件更改为 AnimatedWidget 而不是 StatefulWidget
    • 因为它需要监听ValueNotifier的变化并重建自己。 AnimatedWidget 会自动为您执行此操作。
    • 好东西。而不是打电话给setState?假设我想将其扩展到上述简单用例之外,该模式是什么样的?
    • 你应该做我建议的第一件事(在树的更高级别保持卡片的选择性,即 CardHolder)。据推测,当用户尝试进入应用程序的下一步时,您无论如何都需要能够找出选择了哪些卡片,因此这不会增加复杂性。
    猜你喜欢
    • 2018-11-13
    • 2021-06-27
    • 1970-01-01
    • 1970-01-01
    • 2013-12-29
    • 2021-03-06
    • 1970-01-01
    • 2022-01-01
    • 1970-01-01
    相关资源
    最近更新 更多