加载时如何防止FutureBuilder更改旧窗口小部件?

时间:2020-06-24 12:54:46

标签: flutter

背景:目前,我正在使用远程服务器图像查看器,我使用 FutureBuilder来显示图像和图像之间的切换,它可以正常工作,但是在切换图像之间会在几毫秒内显示空白屏幕。

问题:我想在 FutureBuilder加载时保留旧的图像小部件,或者在旧图像小部件上显示加载圆圈,而不是在加载时显示新的空白加载页面。或者其他没有 Futurebuilder的解决方案(例如可滚动小部件)?

框架代码:

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());
        }
      },
    );
  }
}

2 个答案:

答案 0 :(得分:1)

最后我得到PreloadPageView,参数为physics: new NeverScrollableScrollPhysics()preloadPageController.jumpToPage(index),它完全满足了我的需求,没有任何闪烁。

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);
            }
          },
        )

答案 1 :(得分:0)

Provider将是一个很好的解决方案。

在您的窗口小部件树中,查看器窗口小部件上方的窗口小部件必须看起来像这样

ChangeNotifierProvider<SmbHalfResult>(
  create: (context) {
    var smbHalfResult = SmbHalfResult(); 
    smbHalfResult.fetchImage(0);
    return smbHalfResult; 
  },  
  child: Viewer(); 
)

SmbHalfResult 类的外观应与此类似

class SmbHalfResult extends ChangeNotifier{
  Uint8List image; 
  void fetchImage(int index) async {
    this.image = await downloadImage(index); 
    notifyListeners(); 
  }
}

最后您的实际用户界面必须是这样

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(); 
      }
    );
  }
}