Reputation: 128
Background: Currently I work on a remote server image viewer,I use a FutureBuilder
to show image and swith between image,it work fine,but will show blank screen in some ms between switch images.
Question: I want to remain the old image widget when FutureBuilder
loading or show loading circle over old image widget instead of showing new blank loading page when loading.Or any other solution without Futurebuilder
(like Scrollable Widget)?
the skeleton code:
class Viewer extends StatefulWidget {
Viewer(this.filename, {Key key}) : super(key: key);
final String filename;
@override
_ViewerState createState() {
return _ViewerState();
}
}
class _ViewerState extends State<Viewer> {
int _index = 0;
@override
Widget build(BuildContext context) {
return GestureDetector(
child: Container(
child: showImage(context, _index),
),
onPanDown: (DragDownDetails e) {
//it will change _index to move to next image when tap down
_index+=1;
}
);
}
Widget showImage(BuildContext context, int index) {
return FutureBuilder<SmbHalfResult>(
future: () async {
//load image from remote server or from memory cache,the SmbHalfResult contain a Uint8List image.
return SmbHalfResult();
}(),
builder: (BuildContext context, AsyncSnapshot<SmbHalfResult> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
return Text("Error: ${snapshot.error}");
} else {
//show image.
return Image.memory(snapshot.data.result.content);
}
} else {
// show loading circle,it will new a blank page,but I even want to display old image other than a blank page when loading.
return Center(child: CircularProgressIndicator());
}
},
);
}
}
Upvotes: 0
Views: 978
Reputation: 128
finally I end up with PreloadPageView with paramter physics: new NeverScrollableScrollPhysics()
and preloadPageController.jumpToPage(index)
, it perfectly meet my need without any flicker.
GestureDetector(
child: Container(
child: PreloadPageView.builder(
preloadPagesCount: 5,
itemBuilder: (BuildContext context, int index) =>
FutureImage(index, widget.filename),
controller: preloadPageController,
physics: new NeverScrollableScrollPhysics(),
)),
onPanDown: (DragDownDetails e) {
Offset globalPosition = e.globalPosition;
RenderBox findRenderObject = context.findRenderObject();
Size size = findRenderObject.size;
Area area = getArea(globalPosition, size);
if (area == Area.lef) {
index--;
preloadPageController.jumpToPage(index);
} else if (area == Area.right) {
index++;
preloadPageController.jumpToPage(index);
}
},
)
Upvotes: 1
Reputation: 368
Provider will be good solution for this.
In your widget tree, the widget above the Viewer widget must look like this
ChangeNotifierProvider<SmbHalfResult>(
create: (context) {
var smbHalfResult = SmbHalfResult();
smbHalfResult.fetchImage(0);
return smbHalfResult;
},
child: Viewer();
)
The SmbHalfResult class should look something similar to this
class SmbHalfResult extends ChangeNotifier{
Uint8List image;
void fetchImage(int index) async {
this.image = await downloadImage(index);
notifyListeners();
}
}
And finally your actual UI must be like this
class _ViewerState extends State<Viewer> {
int _index = 0;
@override
Widget build(BuildContext context) {
return GestureDetector(
child: Container(
child: Consumer<SmbHalfResult>(
builder: (context, model) {
if(model.image != null) return Image.memory(model.image);
return CircularProgressIndicator();
}
),
),
onPanDown: (DragDownDetails e) {
Provider.of<SmbHalfResult>(context, listen: false).fetchImage();
}
);
}
}
Upvotes: 0