Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

dart - Flutter:(Looking up a deactivated widget's ancestor is unsafe)

I tried to make a ListView that can slide left and right, the item is SingleChildScrollView, InheritedData used to listener, and encountered the following problem:

Looking up a deactivated widget's ancestor is unsafe.

At this point the state of the widget's element tree is no longer stable.

To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.

When the exception was thrown, this was the stack
#0      Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure>
#1      Element._debugCheckStateIsActiveForAncestorLookup
#2      Element.dependOnInheritedWidgetOfExactType
#3      InheritedData.of
#4      SlideMenuItem.build.<anonymous closure>
...

In order to facilitate the view of the problem, I write the code in the same dart file, the specific implementation is as follows:

import 'package:flutter/material.dart';

typedef SlideMenuBuilder = Widget Function(BuildContext context, int index);
typedef TouchDownCallback = void Function(Offset offset);

class SwipeMemuWidget extends StatefulWidget {
  final List<Widget> child;
  final SlideMenuBuilder builder;
  final int itemCount;

  const SwipeMemuWidget({Key key, this.child, this.builder, this.itemCount})
      : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return _SwipeMenuWidgetState();
  }
}

class _SwipeMenuWidgetState extends State<SwipeMemuWidget> {
  Offset tapDownOffset;
  @override
  Widget build(BuildContext context) {
    return InheritedData(
        tapDownOffset: tapDownOffset,
        child: Listener(
          onPointerDown: (downEvent) {
            setState(() {
              tapDownOffset = downEvent.position;
            });
          },
          child: ListView.builder(
              itemCount: widget.itemCount, itemBuilder: widget.builder),
        ));
  }
}


class InheritedData extends InheritedWidget {
  final Offset tapDownOffset;

  InheritedData({@required this.tapDownOffset, Widget child})
      : super(child: child);

  static InheritedData of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<InheritedData>();
  }

  @override
  bool updateShouldNotify(InheritedData old) => true;
}


class SlideMenuItem extends StatelessWidget {
  SlideMenuItem({this.menuWidth, this.showWidth = 750, this.child, this.menus});
  final Widget child;
  final List<Widget> menus;
  final double menuWidth;
  final double showWidth;

  _buildChildren(BuildContext context) {
    
    List<Widget> childrenWidget = List<Widget>();
    childrenWidget.add(Container(
      width: showWidth,
      child: child,
    ));
    childrenWidget.addAll(menus.map((e) => Container(
          color: Colors.red,
          child: e,
        )));
    return childrenWidget;
  }

  @override
  Widget build(BuildContext context) {
    bool isAnimated = false;
    ScrollController controller = ScrollController();
    WidgetsBinding.instance.addPostFrameCallback((duration) {
      Offset tapDownOffset = InheritedData.of(context).tapDownOffset;
      if (tapDownOffset != null && controller.hasClients) {
        RenderBox renderBox = context.findRenderObject();
        Offset myOffset = renderBox.localToGlobal(Offset(0, 0));
        Size mySize = renderBox.size;
        if (controller.offset > 0 &&
            (showWidth - controller.offset > tapDownOffset.dx ||
                myOffset.dy > tapDownOffset.dy ||
                myOffset.dy + mySize.height < tapDownOffset.dy)) {
          isAnimated = true;
          controller
              .animateTo(0,
                  duration: Duration(milliseconds: 100), curve: Curves.linear)
              .then((v) {
            isAnimated = false;
          });
        }
      }
    });
    return Listener(
        onPointerUp: (upEvent) {
          if (isAnimated) return;
          if (controller.offset < menuWidth / 5) {
            controller.animateTo(0,
                duration: Duration(milliseconds: 100), curve: Curves.linear);
          } else {
            controller.animateTo(menuWidth,
                duration: Duration(milliseconds: 100), curve: Curves.linear);
          }
        },
        child: SingleChildScrollView(
          scrollDirection: Axis.horizontal, 
          controller: controller,
          child: IntrinsicHeight(
            child: Row(
              children: _buildChildren(context),
            ),
          ),
        ));
  }
}

Please help me, thanks very much!

question from:https://stackoverflow.com/questions/65649308/flutterlooking-up-a-deactivated-widgets-ancestor-is-unsafe

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...