2017-05-24 82 views
1

我剛開始玩Flutter/Dart,我在想爲什麼我的Card窗口小部件在屏幕外滾動時正在丟失狀態。Gridview中的窗口小部件在屏幕外失去狀態

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

回答

1

這種行爲是否按預期運行在這個意義上,撲旨在清理你的屏幕外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)) 
    ) 
    ); 
    } 
} 
+0

我認爲這與它有關。快速跟進:你爲什麼將卡片小部件改成「AnimatedWidget」而不是「StatefulWidget」? – opticon

+0

因爲它需要監聽對「ValueNotifier」的更改並重建它自己。 'AnimatedWidget'會自動爲你做。 –

+0

好東西。與調用'setState'相反?假設我想將它擴展到上面的簡單用例之外,那麼這個模式是什麼樣子的? – opticon

相關問題