nillouise
nillouise

Reputation: 128

How to prevent FutureBuilder change old widget when loading?

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

Answers (2)

nillouise
nillouise

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

Prasanna Kumar
Prasanna Kumar

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

Related Questions