Пытаясь увеличить asp использование потоков во Flutter, я последовал интересному примеру, найденному здесь https://github.com/tensor-programming/flutter_streams, который показывает, как получить данные из http-вызова и заполнить лениво (очень большой) список фото объектов / виджетов.
Все сводится к потоку и подписке на него, который вызывает setState для каждого события в потоке, добавляя элемент в список элементов [для отладка, я поставил оператор print("ADD!")
, чтобы убедиться, что вызов работает как задумано. И это так].
Поток «заполняется» после вызова http, который предоставляет большой список необработанных демонстрационных данных. И чтобы увидеть, когда Flutter (пере) строит основную часть виджета PhotoList, я положил хороший print("BUILD!!!")
как раз перед возвращением Scaffold.
Весь код программы флаттера таков:
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Photo Streamer',
theme: ThemeData(
primarySwatch: Colors.green,
),
home: PhotoList(),
);
}
}
class PhotoList extends StatefulWidget {
@override
PhotoListState createState() => PhotoListState();
}
class PhotoListState extends State<PhotoList> {
StreamController<Photo> streamController;
List<Photo> list = [];
@override
void initState() {
super.initState();
streamController = StreamController.broadcast();
streamController.stream.listen((p) => {
setState(() {
list.add(p);
print("ADD!");
})
});
load(streamController);
}
load(StreamController<Photo> sc) async {
String url = "https://jsonplaceholder.typicode.com/photos";
var client = new http.Client();
var req = new http.Request('get', Uri.parse(url));
var streamedRes = await client.send(req);
streamedRes.stream
.transform(utf8.decoder)
.transform(json.decoder)
.expand((e) => e)
.map((map) => Photo.fromJsonMap(map))
.pipe(sc);
}
@override
void dispose() {
super.dispose();
streamController?.close();
streamController = null;
}
@override
Widget build(BuildContext context) {
print("BUILD!!!");
return Scaffold(
appBar: AppBar(
title: Text("Photo Streams"),
),
body: Center(
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) => _makeElement(index),
),
),
);
}
Widget _makeElement(int index) {
if (index >= list.length) {
return null;
}
return Container(
padding: EdgeInsets.all(5.0),
child: Padding(
padding: EdgeInsets.only(top: 200.0),
child: Column(
children: <Widget>[
Image.network(list[index].url, width: 150.0, height: 150.0),
Text(list[index].title),
],
),
));
}
}
class Photo {
final String title;
final String url;
Photo.fromJsonMap(Map map)
: title = map['title'],
url = map['url'];
}
Я ожидал, что каждый раз вызывается setState (добавление элемента в список отображаемых объектов) новая перестройка дерева виджетов будет называться , с отпечатками, похожими на: ДОБАВИТЬ! BUILD !!! ДОБАВИТЬ! BUILD !!! ... (et c.)
Но это не происходит. Что я вижу это: BUILD !!!! ДОБАВИТЬ! ДОБАВИТЬ! ... ... ДОБАВИТЬ! BUILD !!!!
И вот мои вопросы:
1) почему Flutter перестраивает виджеты всего два раза, даже если setState вызывается каждый раз, когда элемент добавляется в объекты списка?
2) если весь список заполняется одним выстрелом перед перерисовкой, какой смысл использовать поток?
и, что не менее важно, в общем случае:
3) если вызов http возвращает ВСЕ ДАННЫЕ сразу (как это происходит), то какой смысл использовать поток для заполнения (длинного) списка элементов, а не для заполнения списка напрямую - после асинхронной / ждать событие было запущено?
[Я старый алгоритмист, и эти вещи сводят меня с ума :)].
Я буду вечно благодарен тому, кто осветит их вопросы! Спасибо.