Flutter 出现 The method 'findRenderObject' was called on null

2024-04-17

最近有一个需求,需要测量条子里的子元素到顶部的距离,但是总是提示findrendereobject为空。 我什至无法尝试 widgetsbinding.instance.addpostframecallback
控制台错误:

════════调度程序库捕获异常 ════════════════════════════════════════ ══════════ ═══
下列 在调度程序回调期间抛出 NoSuchMethodError:该方法 'findRenderObject' 被调用为 null。接收者: null 尝试调用: 查找渲染对象()

请大家看看怎么回事,为什么测不到身高~非常感谢!!! 我的代码:

class GoodsDetailPage extends StatefulWidget {
  final IndexVOS bean;
  GoodsDetailPage(this.bean);
  @override
  _GoodsDetailPageState createState() => _GoodsDetailPageState();
}

class _GoodsDetailPageState extends State<GoodsDetailPage> with SingleTickerProviderStateMixin{
  int productId;
  int areaId;
  String serviceTime;
  ProductInfoBean productInfoBean;
  String _selectName;
  Norms norms;
  double screenWidth;
  bool _isShow = false;
  TabController _tabController;
  List<String> tabList;
  ScrollController _scrollController = ScrollController();
  var globalKeyOne = GlobalKey();
  var globalKeyTwo = GlobalKey();
  var globalKeyThree = GlobalKey();
  var oneY = 0.0;
  var twoY = 0.0;
  var threeY = 0.0;

  ProductModel model = ProductModel();
  GoodsSpecModel _specModel = GoodsSpecModel();


  @override
  void initState() {
    productId = widget.bean.productId;
    areaId = widget.bean.areaId;
    serviceTime = widget.bean.serviceTimeBegin;
    tabList = [
      "商品",
      "评价",
      "详情",
    ];
    _tabController = TabController(
      length: tabList.length,
      vsync: this,
    );
    _scrollController.addListener(() {
      var of = _scrollController.offset;
      setState(() {
        _isShow = of >= screenWidth-50;
      });
      if (of > threeY - oneY) {
        _tabController.animateTo(2);
      }else if (of > twoY - oneY) {
        _tabController.animateTo(1);
      } else {
        _tabController.animateTo(0);
      }
      print("滚动了$of one=${twoY - oneY}=two=${threeY - oneY}");
    });
    WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
    super.initState();
  }

  //等待界面渲染完成
  void _afterLayout(_){
    oneY = getY(globalKeyOne.currentContext);
    twoY = getY(globalKeyTwo.currentContext);
    threeY = getY(globalKeyThree.currentContext);
  }

  static double getY(BuildContext buildContext) {
    final RenderBox box = buildContext.findRenderObject();
    //final size = box.size;
    final topLeftPosition = box.localToGlobal(Offset.zero);
    return topLeftPosition.dy;
  }

  Future _request() async{
    model.requestGoodsDetail(productId: productId,areaId: areaId,serviceTime: serviceTime);
  }

  @override
  Widget build(BuildContext context) {
    screenWidth = MediaQuery.of(context).size.width;
    return Scaffold(
      body: Container(
        width: double.infinity,
        height: double.infinity,
        child: ProviderWidget<ProductModel>(
          model: model,
          onModelReady: (model) => model.requestGoodsDetail(productId: productId,areaId: areaId,serviceTime: serviceTime),
          builder: (context, model, child) {
            if (model.busy) {
              return ViewStateBusyWidget();
            }
            if (model.error) {
              return ViewStateErrorWidget(error: model.viewStateError, onPressed: _request);
            }
            return Column(
              children: <Widget>[
                Expanded(child: _body()),
                _bottom()
              ],
            );
          }
        ),
      )
    );
  }

  Widget _body(){
    var _normalBack = Image.asset(ImagePath.normalBack,height: dp40,width: dp40);
    var _detailBack = Image.asset(ImagePath.detailBack,height: dp60,width: dp60);
    var _normalShare = Image.asset(ImagePath.detailShareSmall,height: dp40,width: dp60);
    var _detailShare = Image.asset(ImagePath.detailShare,height: dp60,width: dp140);
    var _normalDot = Image.asset(ImagePath.threeDot,height: dp40,width: dp40);
    var _detailDot = Image.asset(ImagePath.detailDot,height: dp60,width: dp60);
    return CustomScrollView(
      controller: _scrollController,
      slivers: <Widget>[
        SliverAppBar(
          key: globalKeyOne,
          title: _isShow?_topTabTitle():Container(),
          centerTitle: true,
          expandedHeight: screenWidth,
          floating: false,
          pinned: true,
          snap: false,
          elevation: 0.5,
          leading: IconButton(
            icon: _isShow?_normalBack:_detailBack,
            onPressed: () => pop(),
          ),
          actions: <Widget>[
            GestureDetector(
              child: _isShow?_normalShare:_detailShare,
              onTap: (){
                print("分享");
              },
            ),
            SizedBox(width: dp24),
            GestureDetector(
              child: _isShow?_normalDot:_detailDot,
              onTap: _showPopup,
            ),
            SizedBox(width: dp30),
          ],
          flexibleSpace: FlexibleSpaceBar(
            background: GoodsDetailBannerWidget(model),
          ),
        ),
        SliverList(
          delegate: SliverChildListDelegate([
            _activityImage(),
            GoodsDetailInfoWidget(model),
            gap20,
            GoodsDetailOtherWidget(model,serviceTime),
            GoodsDetailCategoryWidget(click: _clickSpec,name: _selectName),
            gap20,
            Column(
              key: globalKeyTwo,
              children: <Widget>[
                GoodsDetailCommentWidget(model),
                gap20,
              ],
            ),
            Container(
              key: globalKeyThree,
              child: GoodsDetailDescWidget(model),
            )
          ]),
        ),
      ],
    );
  }

  Widget _bottom(){
    return GoodsDetailBottomWidget(
      clickBuy: (){
        if(_selectName == null){
          _clickSpec();
          return;
        }
        routePush(ConfirmOrderPage(productInfoBean: model.productInfoBean,norms: norms,count: _specModel.countNumber));
      },
    );
  }

  Widget _activityImage(){
    return Container(
      width: double.infinity,
      height: dp80,
      child: Image.asset(ImagePath.goodsActivity),
    );
  }

  Widget _topTabTitle(){
    return Container(
      height: dp60,
      child: TabBar(
        controller: _tabController,
        labelColor: ColorConfig.themeGreen,
        unselectedLabelColor: ColorConfig.C09,
        labelStyle: StyleConfig.green_36Style,
        unselectedLabelStyle: StyleConfig.normalTitle36Style,
        indicatorColor: ColorConfig.themeGreen,
        indicatorSize: TabBarIndicatorSize.label,
        indicatorWeight: 2,
        isScrollable: true,
        labelPadding: EdgeInsets.symmetric(horizontal: dp20),
        tabs: tabList.map((item) {
          return Tab(
            text: item,
          );
        }).toList(),
        onTap: (index){
          switch(index){
            case 0:
              _scrollController.jumpTo(0);
              _tabController.animateTo(0);
              break;
            case 1:
              _scrollController.jumpTo(twoY - oneY);
              _tabController.animateTo(1);
              break;
            case 2:
              _scrollController.jumpTo(threeY - oneY);
              _tabController.animateTo(2);
              break;
          }
        },
      ),
    );
  }

  void _showPopup(){
    showPopupWindow(
      context,
      gravity: KumiPopupGravity.rightTop,
      underStatusBar: true,
      underAppBar: true,
      offsetX: -dp30,
      offsetY: -dp20,
      childFun: (pop){
        return Container(
          key: GlobalKey(),
          child: GoodsDetailPopupMenuWidget(),
        );
      }
    );
  }


  void _clickSpec(){
    _specModel.initData(model.normInfoBean.norms);
    showModalBottomSheet(
      context: context,
      isScrollControlled: true,
      backgroundColor: Colors.transparent,
      builder: (BuildContext context) {
        return GoodsSpecSelectDialog(
          model: _specModel,
          onSelected: (bean, count) {
            norms = bean;
            setState(() {
              _selectName = "已选:"+bean.normName + ",数量:" + count.toString();
            });
          },
        );
      },
    );
  }

  @override
  void dispose() {
    _specModel.dispose();
    _scrollController.dispose();
    _tabController.dispose();
    super.dispose();
  }

}

您的全局密钥已附加到以下小部件中_body()方法,但此方法仅在以下情况下被调用model.busy = false and model.error = false。它的意思是globalKeyOne.currentContext将为空,当model.busy = true || model.error = true. Your _afterLayout(...)在所有情况下都会被调用,这就是 NPE 失败的原因。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Flutter 出现 The method 'findRenderObject' was called on null 的相关文章

随机推荐