最近我在像项目这样的画廊工作,该画廊显示了数据库中的一堆图像。我使用MySQL是因为我拥有专用服务器,并且由于价格原因,使用Firestore可能不是一个好主意。我希望它具有一个无限滚动视图,每次可以加载10张图像,但是目前它同时加载所有图像,这使应用程序感觉真的很慢。
这是我的lib / main.dart
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:http/http.dart' as http;
//local import, you can ignore it
import './nav.dart';
import './detail.dart';
import './placeholder.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyApp createState() => new _MyApp();
}
class _MyApp extends State<MyApp> {
Future<List> getData() async {
final resp = await http.get("http://192.168.43.68/API/readImage.php");
return json.decode(resp.body);
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return new MaterialApp(
theme: new ThemeData.dark(),
home: Scaffold(
appBar: new AppBar(
title: new Text("Title"),
),
drawer: Nav(),
body: new FutureBuilder(
future: getData(),
builder: (context, snapshot) {
if (snapshot.hasError) {
print(snapshot.error);
}
return snapshot.hasData
? new ItemList(
list: snapshot.data,
)
: new Center(child: new CircularProgressIndicator());
},),),);}}
//Item builder to display each image
class ItemList extends StatelessWidget {
final List list;
ItemList({this.list});
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Padding(
padding: const EdgeInsets.all(0.0),
child: new StaggeredGridView.countBuilder(
crossAxisCount: 4,
itemCount: list.length,
itemBuilder: (BuildContext context, int index) => new Container(
child: new GestureDetector(
onTap: () => Navigator.of(context).push(new MaterialPageRoute(
builder: (BuildContext context) => new Detail(
list: list,
index: index,
))),
child: Card(
child: new Column(
children: <Widget>[
new Stack(
children: <Widget>[
FadeInImage.memoryNetwork(
placeholder: kTransparentImage,
image: list[index]['url'],
),],),
new Padding(
padding: EdgeInsets.all(4.0),
child: new Column(
children: <Widget>[
new Text(list[index]['name'],
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white70,
))],),)],),))),
staggeredTileBuilder: (index) => new StaggeredTile.fit(2),
),);}}
这是我的readImage.php(我仍然不知道该怎么办)
<?php
include 'dbcon.php';
$sth = "SELECT * FROM pict INNER JOIN user ON pict.uid=user.uid LIMIT 10";
$result = $conn->query($sth);
$outp = array();
$outp = $result->fetch_all(MYSQLI_ASSOC);
echo json_encode($outp);
enter code here
仅此而已,任何回应将不胜感激。
答案 0 :(得分:1)
我相信您遇到的主要问题是您正在使用StaggeredTile.fit()。
一些背景信息:在抖动中,图像大小不是立即确定的,而是在图像实际加载后确定的。这对于少量的图像很好,但是当数量较大(可能无限)时,会引起问题。
这样做的原因是,在第一次构建列表时,它会布局足以适合屏幕的子级(+ cacheExtent,这可能不适用于StaggeredGridView,但适用于大多数抖动列表)。考虑一下列表中的每个项目将如何布局-GestureDetector,Card,Column和Stack为其子代分配的所有委托大小。因此,大小取决于图像。占位符并不能真正确定高度,因此您可以想到高度为零或一的图像。
这意味着在您的页面中,随着越来越多的项目添加到屏幕上,它不断尝试加载越来越多的图像-垂直像素数或实际上是无限的,这取决于占位符高度的精确度
对此有一个简单的解决方案,但它可能对您没有吸引力-而不是使用StaggeredTile.fit(),而是使用固定的高度,然后使图像适合该高度。
不幸的是,如果您希望每个图像都具有不同的高度,它将变得更加复杂。解决该问题的一种方法(因为您可以控制数据库)是将纵横比以及每个图像的路径存储在数据库中。这样,您可以使用AspectRatio小部件包装FaceInImage,因此可以立即确定高度。
这当然只有在您的项目列表不是真正无限的情况下才有效,但是由于它在数据库中,所以我认为假定= D是相当安全的。您可以在PHP中使用the getimagesize function来计算每个图像的长宽比,然后一次在整个数据库中运行,然后从现在开始,每当插入新图像时,都必须计算长宽比。
另一种解决方案是从您指定的固定高度开始,然后在加载图像后更改高度。但是,我不知道StaggeredGridView会如何处理-我见过的大多数列表/网格类都希望他们的孩子保持相同的大小。在这种情况下,您可能必须查看CustomScrollView并编写自己的条子,这超出了此问题的范围。
编辑:OP阐明了MySQL部分显然是他们要问的,而不是为什么它运行缓慢。我仍然认为图像是为什么它慢而不是对URL的请求的原因,但是我可能是错的。尚未指定是要加载10、50、100还是1000张图像。
在没有更多有关如何设置数据库的知识的情况下,很难就是否存在问题给出明确的答案。但是,我将快速概述一下设置服务器的可能方法。
我不会为您的服务器编写实际的代码,因为这超出了关于抖动的SO问题的范围。如果您不知道该如何解决,我建议您先找到一门有关设置服务器的课程或教程,然后再问一个不是用flutter而是用PHP / MySQL等标记的问题确实是您要问的问题,而不是一般的“我无法帮助我”,是关于您遇到的具体问题,因为那样就无法得到答案。
假设以下内容:
在服务器端,您将必须设置一个端点来响应请求并返回数据部分。如果您想使用JSON,我希望没有参数的端点返回类似以下的内容:
{
num: 30,
images: [
{ title: ..., url: ..., .... },
...
],
more: true
}
该列表必须按某些参数进行排序-标题,日期,ID等。您也可以使用totalItems
作为返回结果的一部分来告诉客户,其中有多少图像。您也可以选择接受要退回的商品数量。
对于后续请求,您可以接受参数num
和offset
。偏移量应该是描述最后返回的结果的一种方式,以便查询知道从何处开始-如果按ID排序,则可能是那个,或者按日期,可能是日期。而num
将是要返回的最大结果数。
如果您不将totalItems作为响应的一部分返回,则可以选择有一个不同的请求,该请求仅向您提供最大数量的项目,因为这可能会使您的生活变得更轻松。如果您不想执行任何一项操作,请查看the link someone posted in a comment,其中介绍了如何编写无限加载列表。
请注意,这只会返回图片的网址,而不是图片本身。
接下来,您将需要建立某种系统来缓存Flutter代码。这可能会变得有些复杂,因为在页面滚动时,您不仅必须获取图像,而且还需要获取图像的描述。
我将如何操作如下:
还有另一种方法可以执行此操作,但是对于您的数据库可能并不是特别理想(取决于它的设置方式)。您无需在客户端进行缓存,只需向服务器发送每个图像的请求(即GET user / images / 1,user / images / 2等)。然后,您的服务器将在内部确定从何处加载该图像,并以标题等作为标题直接返回它。
这样做的问题是,幼稚的方法是对每个传入的请求都进行数据库请求。这会起作用,但会导致对数据库的大量请求。根据您的数据库设置,可能还可以,但是最终很可能会导致问题。相反,您可以在服务器上批量处理请求(即,如果请求为0,也获取并缓存0-30),然后只需对缓存的数据进行处理即可。我将把它留给您实施,因为它超出了此问题的范围。