首页 > 解决方案 > 重新打开使用 StreamBuilder 的 ExpansionTile 时出现错误的流状态

问题描述

第一次打开ExpansionTitle,一切都很好。在缩小和扩大它之后,我收到了一个“坏状态”流已经听过,错误。我尝试将流作为广播返回,但在最小化并重新打开后,快照为空。我不太确定将 ExpansionTitle 与流一起使用的正确技术是什么。

import 'package:flutter/material.dart';

void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
  return MaterialApp(
  title: 'Flutter Demo',
  home: MyHomePage(title: 'Flutter Demo Home Page'),
 );
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;

@override
 _MyHomePageState createState() => _MyHomePageState();
  }
   class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
  appBar: AppBar(
    title: Text(widget.title),
  ),
  body: Center(
    child: StreamBuilder(
        stream: _genres(),
        builder:
            (BuildContext context, AsyncSnapshot<List<String>> snapshot) {
          print("snap:  ${snapshot.data}");
          if (snapshot.hasData) {
            return ListView.builder(
                itemCount: snapshot.data.length,
                itemBuilder: (BuildContext context, int index) {


              return ExpansionTile(
                title: Text(snapshot.data[index]),
                children: <Widget>[
                  StreamBuilder(
                    stream: _moviemap(index + 1),
                    builder: (BuildContext context, AsyncSnapshot snapshot) {
                      if (!snapshot.hasData)
                        return Text('No data');


                      List<Widget> items = [];
                      for(String name in snapshot.data) {
                        items.add(
                          ListTile(
                            title: Text(name),
                          )
                        );
                      }

                      return Column(children: items,);
                    },
                  ),
                ],
              );
            });
          } else {
            return Container();
          }
        }),
  ),
 );
}
   Stream<List<String>> _genres() async* {
     yield ['Horror', 'Comedy', 'Drama'];
   }

   Stream<List<String>> _moviemap(int which) async* {
   if (which == 1) {
      yield ['Saw', 'Scary Movie'];
    } else if (which == 2) {
      yield ['Grown ups', 'Grown ups 2', 'Paul Blart'];
    } else {
      yield ['Green Onions', 'Spring Breakers'];
    }
 }
 }

标签: dartflutter

解决方案


是因为以下几个原因。

  1. StreamBuilder从's到 .永久监听相同的流(_moviemap在这种情况下)。StreamBuilderinitState()dispose()
  2. 打开和关闭时ExpansionTile,它会重建children新的StreamBuilder. 并StreamBuilder尝试倾听_moviemap。那个时候老的StreamBuilder还没有完成dispose()。
  3. 同一个 stream( _moviemap) 不允许多次收听。
  4. 所以崩溃了。

所以你应该交换StreamBuilder' 位置和ExpansionTile' 位置来监听 stream( _moviemap) 一次。祝你好运!!

示例代码:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

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

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: StreamBuilder(
            stream: _genres(),
            builder:
                (BuildContext context, AsyncSnapshot<List<String>> snapshot) {
              print("snap:  ${snapshot.data}");
              if (snapshot.hasData) {
                return ListView.builder(
                  itemCount: snapshot.data.length,
                  itemBuilder: (BuildContext context, int index) {
                    final title = snapshot.data[index];
                    return StreamBuilder(
                      stream: _moviemap(index + 1),
                      builder: (BuildContext context, AsyncSnapshot snapshot) {
                        if (!snapshot.hasData) return Text('No data');
                        List<Widget> items = [];
                        for (String name in snapshot.data) {
                          items.add(ListTile(
                            title: Text(name),
                          ));
                        }
                        return ExpansionTile(
                          title: Text(title),
                          children: <Widget>[
                            Column(
                              children: items,
                            ),
                          ],
                        );
                      },
                    );
                  },
                );
              } else {
                return Container();
              }
            }),
      ),
    );
  }

  Stream<List<String>> _genres() async* {
    yield ['Horror', 'Comedy', 'Drama'];
  }

  Stream<List<String>> _moviemap(int which) async* {
    if (which == 1) {
      yield ['Saw', 'Scary Movie'];
    } else if (which == 2) {
      yield ['Grown ups', 'Grown ups 2', 'Paul Blart'];
    } else {
      yield ['Green Onions', 'Spring Breakers'];
    }
  }
}


推荐阅读