【问题标题】:Flutter, "Stream has already been listened to" error in GridView [duplicate]Flutter,GridView中的“Stream已被收听”错误[重复]
【发布时间】:2023-03-29 20:06:01
【问题描述】:

我正在做的是获取卡通列表并通过 GridView 显示。下面的代码正在获取数据

Future<void> _getWebtoonData() async {
    var response;
    if(_daysReceivedResponse[_pressedButtonDayIndex]){
      response = _daysResponse[_pressedButtonDayIndex];
    } else {
      response= await http.get('https://comic.naver.com/webtoon/weekdayList.nhn?week='+_currentWebtoonAddress);
      _daysReceivedResponse[_pressedButtonDayIndex] = true;
      _daysResponse[_pressedButtonDayIndex] = response;
    }
    dom.Document document = parser.parse(response.body);
    final e1 = document.querySelectorAll('.img_list .thumb');
    final e2 = document.querySelectorAll('.img_list .desc');
    final e3 = document.querySelectorAll('.img_list .rating_type');

    List<List<String>> infoCollection = List<List<String>>();
    List<String> info = List<String>();

    for(int i=0; i<e1.length; i++){
      info.add(e1[i].getElementsByTagName('img')[0].attributes['src']);
      info.add(e1[i].getElementsByTagName('a')[0].attributes['title']);
      info.add(e2[i].getElementsByTagName('a')[0].innerHtml);
      info.add(e3[i].getElementsByTagName('strong')[0].innerHtml);
      infoCollection.add(info);
    }
    _controller.sink.add(infoCollection);
  }

我正在通过 GridView 显示这些图片、标题、艺术家和评分,如下所示

Widget _getWebtoonGridView() {
    return StreamBuilder(
      stream: _controller.stream.asBroadcastStream(),
      builder: (BuildContext context, AsyncSnapshot<List> snapshot){
        if(snapshot.hasError)
          print(snapshot.error);
        else if(snapshot.hasData){
          return GridView.count(
            crossAxisCount: 3,
            childAspectRatio: 0.6,
            children: List.generate(snapshot.data.length, (index){
              return _getWebtoonInfo(index, snapshot.data[index]);
            }),
          );
        }
        else if(snapshot.connectionState != ConnectionState.done)
          return Center(child: CircularProgressIndicator());
      },
    );
  }

但是连续出现“Stream has been listened”错误,我的StreamController是什么问题??

我该如何解决?

堆栈跟踪

I/flutter (21411): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (21411): The following StateError was thrown building Expanded(flex: 1):
I/flutter (21411): Bad state: Stream has already been listened to.
I/flutter (21411):
I/flutter (21411): When the exception was thrown, this was the stack:
I/flutter (21411): #4  _StreamBuilderBaseState._subscribe (package:flutter/src/widgets/async.dart:135:37)
I/flutter (21411): #5  _StreamBuilderBaseState.initState (package:flutter/src/widgets/async.dart:109:5)
I/flutter (21411): #6  StatefulElement._firstBuild(package:flutter/src/widgets/framework.dart:3830:58)
I/flutter (21411): #7  ComponentElement.mount(package:flutter/src/widgets/framework.dart:3696:5)
I/flutter (21411): #8  Element.inflateWidget (package:flutter/src/widgets/framework.dart:2950:14)
I/flutter (21411): #9  Element.updateChild(package:flutter/src/widgets/framework.dart:2753:12)

流控制器变量

StreamController<List<List<String>>> _controller = StreamController<List<List<String>>>.broadcast();

【问题讨论】:

  • 堆栈跟踪(前几帧)是什么?
  • @pskink 我添加了
  • 你的StreamController怎么样?
  • @pskink 我添加了
  • 那么为什么需要.asBroadcastStream()?删除它,hot restart 你的应用,看看会发生什么

标签: gridview controller flutter


【解决方案1】:

如果需要多次收听同一个流,则该流只需要使用广播。

@override
Widget build(BuildContext context) {
  return Column(
    children: [
      Expanded(
        flex: 1,
        child: StreamBuilder()),
      Expanded(
        flex: 1,
        child: StreamBuilder()),
    ],
  );
}

我似乎无法在自己的实现中复制相同的错误,即在 GridView 上显示来自网络的图像。

下面示例中的 Stream 不需要使用 Broadcast 来刷新 Stream,因为它是由单个客户端监听的。 GridView 可以通过触发RefreshIndicator onRefresh 来刷新。在此示例中连续调用 Future&lt;T&gt;().then((response) =&gt; StreamController.add(response)); 不会导致 Bad state: Stream has already been listened to. 错误。

如果您可以提供我可以在本地运行的完整最小重现,我可以帮助检查可能导致 Stream 引发错误的原因。

这是您可以尝试的完整示例。拉动页面刷新和重置分页,滚动到页面底部加载下一张图片。示例中显示的图像来自https://jsonplaceholder.typicode.com/photos

import 'dart:async';
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      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> {
  var _streamController = StreamController<List<Album>>();
  var _scrollController = ScrollController();

  // GridView has 3 columns set
  // Succeeding pages should display in rows of 3 for uniformity
  loadMoreImages(bool increment) {
    setState(() {
      if(!increment) _imageGridCursorEnd = 21;
      else _imageGridCursorEnd += 21;
    });
  }

  // Call to fetch images
  // if refresh set to true, it will trigger setState() to reset the GridView
  loadImages(bool refresh){
    fetchAlbum().then((response) => _streamController.add(response));
    if(refresh)loadMoreImages(!refresh); // refresh whole GridView
  }

  @override
  void initState() {
    super.initState();
    loadImages(false);
    _scrollController.addListener(() {
      if (_scrollController.position.atEdge) {
        if (_scrollController.position.pixels == 0)
          print('Grid scroll at top');
        else {
          print('Grid scroll at bottom');
          loadMoreImages(true);
        }
      }
    });
  }

  @override
  void dispose() {
    super.dispose();
    _streamController.close();
  }

  var _imageGridCursorStart = 0, _imageGridCursorEnd = 21;

  @override
  Widget build(BuildContext context) {
    return StreamBuilder(
      stream: _streamController.stream,
      builder: (BuildContext context, AsyncSnapshot<List<Album>> snapshot) {
        if (snapshot.hasData) {
          // This ensures that the cursor won't exceed List<Album> length
          if (_imageGridCursorEnd > snapshot.data.length)
            _imageGridCursorEnd = snapshot.data.length;
          debugPrint('Stream snapshot contains ${snapshot.data.length} item/s');
        }
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Center(
            child: RefreshIndicator(
              // onRefresh is a RefreshCallback
              // RefreshCallback is a Future Function().
              onRefresh: () async => loadImages(true),
              child: snapshot.hasData
                  ? GridView.count(
                      physics: AlwaysScrollableScrollPhysics(),
                      controller: _scrollController,
                      primary: false,
                      padding: const EdgeInsets.all(20),
                      crossAxisSpacing: 10,
                      mainAxisSpacing: 10,
                      crossAxisCount: 3,
                      children: getListImg(snapshot.data
                          .getRange(_imageGridCursorStart, _imageGridCursorEnd)
                          .toList()),
                    )
                  : Text('Waiting...'),
            ),
          ),
        );
      },
    );
  }

  Future<List<Album>> fetchAlbum() async {
    final response =
        await http.get('https://jsonplaceholder.typicode.com/photos');

    if (response.statusCode == 200) {
      // If the server did return a 200 OK response,
      // then parse the JSON.
      Iterable iterableAlbum = json.decode(response.body);
      var albumList = List<Album>();
      List<Map<String, dynamic>>.from(iterableAlbum).map((Map model) {
        // Add Album mapped from json to List<Album>
        albumList.add(Album.fromJson(model));
      }).toList();
      return albumList;
    } else {
      // If the server did not return a 200 OK response,
      // then throw an exception.
      throw Exception('Failed to load album');
    }
  }

  getListImg(List<Album> listAlbum) {
    final listImages = List<Widget>();
    for (var album in listAlbum) {
      listImages.add(
        Container(
          padding: const EdgeInsets.all(8),
          child: Image.network(album.albumThumbUrl, fit: BoxFit.cover),
          // child: Thumbnail(image: imagePath, size: Size(100, 100)),
        ),
      );
    }
    return listImages;
  }
}

class Album {
  final int albumId;
  final int id;
  final String title;
  final String albumImageUrl;
  final String albumThumbUrl;

  Album(
      {this.albumId,
      this.id,
      this.title,
      this.albumImageUrl,
      this.albumThumbUrl});

  factory Album.fromJson(Map<String, dynamic> json) {
    return Album(
      albumId: json['albumId'],
      id: json['id'],
      title: json['title'],
      albumImageUrl: json['url'],
      albumThumbUrl: json['thumbnailUrl'],
    );
  }
}

演示

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-01
    • 1970-01-01
    • 2021-07-04
    • 2021-12-26
    • 2021-03-06
    • 2019-05-19
    • 2020-05-22
    • 2023-02-16
    相关资源
    最近更新 更多