유지 참고 사항

16432 단어 beginnersflutter
최근에 ListView에 이미지를 유지하는 예를 테스트했습니다. AutomaticKeepAliveClientMixin를 사용하면 간단히 해결할 수 있습니다.

이미지 유지 예제 클래스입니다.

class ImageKeepAliveWidget extends StatefulWidget {
  const ImageKeepAliveWidget({Key? key}) : super(key: key);

  @override
  State<ImageKeepAliveWidget> createState() => _ImageKeepAliveWidgetState();
}

/// use [AutomaticKeepAliveClientMixin] keeping the state
class _ImageKeepAliveWidgetState extends State<ImageKeepAliveWidget>
    with AutomaticKeepAliveClientMixin<ImageKeepAliveWidget> {
  String? _imageUrl;

  @override
  void initState() {
    super.initState();
    _fetchUrl();
  }

  void _fetchUrl() async {
    try {
      http.Response res =
          await http.get(Uri.parse('https://dog.ceo/api/breeds/image/random'));
      _imageUrl = json.decode(res.body)['message'];
      setState(() {});
    } catch (e) {
      debugPrint('error in getting image url');
    }
  }

  @override
  Widget build(BuildContext context) {
    /// must add super.build(context) to preserve state
    super.build(context);
    if (_imageUrl?.isNotEmpty == true) {
      return Image.network(_imageUrl!);
    } else {
      return const CircularProgressIndicator();
    }
  }

  @override
  bool get wantKeepAlive => true;
}


그러나 메커니즘은 어떻게 작동합니까? 먼저 ListView를 살펴보겠습니다.

ListViewaddAutomaticKeepAlives를 추적하면 ListView 자식 대리자가 사용 중SliverChildListDelegate을 찾을 수 있습니다. 빌드 기능 내에서 addAutomaticKeepAlives가 true인지 확인할 수 있으며 하위 위젯은 AutomaticKeepAlive 위젯으로 래핑됩니다.

@override
  Widget? build(BuildContext context, int index) {
    /// ...
    if (addAutomaticKeepAlives)
      child = AutomaticKeepAlive(child: child);
    return KeyedSubtree(key: key, child: child);
  }

AutomaticKeepAlive는 무엇을 할까요?
_AutomaticKeepAliveState를 확인하면 이 stateful 위젯이 업데이트될 때 _updateChild가 호출되는 것을 볼 수 있습니다. KeepAliveNotification을 수신하기 위해 NotificationListener를 래핑합니다. 그리고 나중에 수신기 위젯에 대한 KeepAlive 위젯을 래핑합니다.

@override
void initState() {
  super.initState();
  _updateChild();
}

@override
void didUpdateWidget(AutomaticKeepAlive oldWidget) {
  super.didUpdateWidget(oldWidget);
  _updateChild();
}

void _updateChild() {
  _child = NotificationListener<KeepAliveNotification>(
    onNotification: _addClient,
    child: widget.child!,
  );
}

@override
Widget build(BuildContext context) {
  assert(_child != null);
  return KeepAlive(
    keepAlive: _keepingAlive,
    child: _child!,
  );
}


알림 콜백이 리스너 위젯에서 무엇을 했는지 확인해 봅시다.

요컨대 콜백 함수에는 두 가지 주요 목표가 있습니다. 하나는 맵을 사용하여 KeepAliveNotification 핸들러를 저장하는 것입니다. 다른 하나는 나중에KeepAlive 위젯인 자식에 부모 데이터를 적용하는 것입니다.

bool _addClient(KeepAliveNotification notification) {
  final Listenable handle = notification.handle;
  _handles ??= <Listenable, VoidCallback>{};
  assert(!_handles!.containsKey(handle));
  _handles![handle] = _createCallback(handle);
  handle.addListener(_handles![handle]!);
  if (!_keepingAlive) {
    _keepingAlive = true;
    final ParentDataElement<KeepAliveParentDataMixin>? childElement = _getChildElement();
    if (childElement != null) {
      // If the child already exists, update it synchronously.
      _updateParentDataOfChild(childElement);
    } else {
      // If the child doesn't exist yet, we got called during the very first
      // build of this subtree. Wait until the end of the frame to update
      // the child when the child is guaranteed to be present.
      SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
        if (!mounted) {
          return;
        }
        final ParentDataElement<KeepAliveParentDataMixin>? childElement = _getChildElement();
        assert(childElement != null);
        _updateParentDataOfChild(childElement!);
      });
    }
  }
  return false;
}

ParentDataElement<KeepAliveParentDataMixin>? _getChildElement() {
    ///...
}

KeepAlive 위젯에서 부모 데이터가 applyParentData의 알림으로 renderObject keepAlive 상태를 설정하는 방법에 대한 AutomaticKeepAliveClientMixin 함수를 볼 수 있습니다. keepAlive가 아니면 renderObject가 레이아웃을 다시 실행합니다.

@override
void applyParentData(RenderObject renderObject) {
  assert(renderObject.parentData is KeepAliveParentDataMixin);
  final KeepAliveParentDataMixin parentData = renderObject.parentData! as KeepAliveParentDataMixin;
  if (parentData.keepAlive != keepAlive) {
    parentData.keepAlive = keepAlive;
    final AbstractNode? targetParent = renderObject.parent;
    if (targetParent is RenderObject && !keepAlive)
      targetParent.markNeedsLayout(); // No need to redo layout if it became true.
  }
}


요약하면 전체 프로세스는 다음과 같습니다.

좋은 웹페이지 즐겨찾기