我试图进行简单的分页,但是我的GridView的每个页面都生成两次。然后,我尝试记录代码中发生的事情。我认为我的问题是由在获取方法中设置状态引起的,但是我不确定,我也不知道如何避免这种情况。这是代码:
import 'dart:convert';
import 'dart:async';
import 'package:flutter/material.dart';
import 'dart:developer' as developer;
import 'package:test_webant/data/photoEntity.dart';
import 'package:http/http.dart' as http;
import 'package:test_webant/data/SingleImage.dart';
final galleryUrl = "http://gallery.dev.webant.ru/api/photos?popular=true&page=";
int page = 1;
bool isLoading = false;
class PopularGalleryGrid extends StatefulWidget{
@override
State<StatefulWidget> createState(){
return _PopularGalleryGridState();
}
}
class _PopularGalleryGridState extends State<PopularGalleryGrid>{
List<Photo> data = new List<Photo>();
ScrollController _scrollController = new ScrollController();
Future<List<Photo>> _fetchPhotos() async {
if (!isLoading) {
developer.log('fetch photos');
final response = await http.get(
galleryUrl + page.toString() + "&limit=10");
Map<String, dynamic> decodedJson = json.decode(response.body);
List photos = decodedJson['data'] as List;
return (photos.map((photo) => Photo.fromJson(photo))).toList();
}
setState(() {
isLoading = false;
});
}
@override
void initState(){
data.clear();
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
developer.log('scroll controller');
setState(() {
isLoading = true;
});
page++;
_fetchPhotos();
}
});
super.initState();
}
@override
void dispose(){
super.dispose();
_scrollController.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<List<Photo>>(
future: _fetchPhotos(),
builder:(context, snapshot){
developer.log('builder');
if (snapshot.hasData){
List<Photo> tempList = snapshot.data;
data.addAll(tempList);
return _photoGridView(data);
}
else if (snapshot.hasError) {
return Text("${snapshot.hasError}");
}
return CircularProgressIndicator();
},
)
);
}
GridView _photoGridView(data){
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.0,
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
),
itemCount: data.length,
itemBuilder: (context, index){
return Card(
elevation: 4,
child: GestureDetector(
onTap: () => _navigateToImage(context, data[index].id),
child:
Image.network(('http://gallery.dev.webant.ru/media/' + data[index].image)),
),
);
},
controller: _scrollController,
);
}
void _navigateToImage(BuildContext context, int id) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SingleImage(imageId: id),
),
);
}
}
这就是我在日志中得到的:
[log] fetch photos
[log] builder
[log] builder
I/ple.test_weban(15020): ProcessProfilingInfo new_methods=539 is saved saved_to_disk=1 resolve_classes_delay=8000
[log] scroll controller
[log] fetch photos
[log] builder
[log] builder
答案 0 :(得分:0)
通过调用setState
,您正在重建FutureBuilder
。
FutureBuilder
在构建后执行future,并等待其解决。
只需执行此操作(删除isLoading
逻辑):
Future<List<Photo>> _fetchPhotos() async {
developer.log('fetch photos');
final response = await http.get(
galleryUrl + page.toString() + "&limit=10");
Map<String, dynamic> decodedJson = json.decode(response.body);
List photos = decodedJson['data'] as List;
return (photos.map((photo) => Photo.fromJson(photo))).toList();
}
isLoading
在这里是没有用的,因为如果您不在其中调用setState
,那么FutureBuilder
将仅被构建一次,而_fetchPhotos
也将被调用一次
编辑:使用流
这是您可以对流进行的测试。请注意,由于我没有您的项目,因此无法测试代码,因此您可能希望将其用作指导。
import 'dart:convert';
import 'dart:async';
import 'package:flutter/material.dart';
import 'dart:developer' as developer;
import 'package:test_webant/data/photoEntity.dart';
import 'package:http/http.dart' as http;
import 'package:test_webant/data/SingleImage.dart';
final galleryUrl = "http://gallery.dev.webant.ru/api/photos?popular=true&page=";
int page = 1;
class PopularGalleryGrid extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _PopularGalleryGridState();
}
}
class _PopularGalleryGridState extends State<PopularGalleryGrid> {
List<Photo> data = new List<Photo>();
ScrollController _scrollController = new ScrollController();
StreamController<List<Photo>> _photosStreamController =
StreamController<List<Photo>>.broadcast();
Future<List<Photo>> _fetchPhotos() async {
developer.log('fetch photos');
try {
final response =
await http.get(galleryUrl + page.toString() + "&limit=10");
Map<String, dynamic> decodedJson = json.decode(response.body);
List photos = decodedJson['data'] as List;
List<Photo> result =
(photos.map((photo) => Photo.fromJson(photo))).toList();
data.addAll(result);
_photosStreamController.sink.add(data);
} catch (e) {
_photosStreamController.sink.addError(e);
}
}
@override
void initState() {
data.clear();
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
developer.log('scroll controller');
page++;
_fetchPhotos();
}
});
_photosStreamController.onListen = _fetchPhotos;
super.initState();
}
@override
void dispose() {
super.dispose();
_scrollController.dispose();
_photosStreamController?.close();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<List<Photo>>(
stream: _photosStreamController.stream,
builder: (context, snapshot) {
developer.log('builder');
if (snapshot.hasData || data.isNotEmpty) {
return _photoGridView(snapshot.data ?? data);
} else if (snapshot.hasError) {
return Text("${snapshot.hasError}");
}
return CircularProgressIndicator();
},
));
}
GridView _photoGridView(data) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.0,
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
),
itemCount: data.length,
itemBuilder: (context, index) {
return Card(
elevation: 4,
child: GestureDetector(
onTap: () => _navigateToImage(context, data[index].id),
child: Image.network(
('http://gallery.dev.webant.ru/media/' + data[index].image)),
),
);
},
controller: _scrollController,
);
}
void _navigateToImage(BuildContext context, int id) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SingleImage(imageId: id),
),
);
}
}