Что я делаю, так это получаю список мультфильмов и показываю их с помощью 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());
},
);
}
Но ошибка «Поток уже прослушан» возникает постоянно, в чем проблема моего 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
StreamController<List<List<String>>> _controller = StreamController<List<List<String>>>.broadcast();
@pskink я добавил
как тебе твой StreamController?
@pskink я добавил
так зачем тебе .asBroadcastStream()? удалите его, hot restart ваше приложение и посмотрите, что произойдет
те же ошибки .....
ты делал hot restart? не hot reaload
Понятно, но возникла другая проблема. Как мне использовать StreamBuilder в GridView? Сам GridView или элемент чего?
я понятия не имею, что ты имеешь в виду
см. мой код выше





Stream должен использовать широковещательную рассылку только в том случае, если один и тот же поток нужно прослушивать несколько раз.
т.е.
@override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
flex: 1,
child: StreamBuilder()),
Expanded(
flex: 1,
child: StreamBuilder()),
],
);
}
Кажется, я не могу воспроизвести ту же ошибку в моей собственной реализации отображения изображений из сети в GridView.
Поток в приведенном ниже примере не должен использовать широковещательную рассылку для обновления потока, поскольку его прослушивает один клиент. GridView можно обновить, запустив RefreshIndicator onRefresh. Непрерывный вызов Future<T>().then((response) => 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'],
);
}
}
Демо
что такое stacktrace (первые несколько кадров)?