Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

请问配合上SliverStickyHeader该如何使用? #45

Closed
Dabbit-Chan opened this issue Aug 28, 2023 · 15 comments
Closed

请问配合上SliverStickyHeader该如何使用? #45

Dabbit-Chan opened this issue Aug 28, 2023 · 15 comments

Comments

@Dabbit-Chan
Copy link

现在有一个CustomScrollView长这样

[
  SliverAppBar
  SliverPersistentHeader
  ...List.generate(**, (index){
    return SliverStickyHeader(
      header: ***,
      sliver: SliverList(
        delegate: SliverChildBuilderDelegate((context, index) {
           _sliverListCtx ??= context;
           return ***;
         }, childCount: ***,
       ),
      ),
    );
  })
]

假设我第一个SliverStickyHeader底下的list长度为4,第二个SliverStickyHeader底下的list长度为3,我这样给_sliverListCtx赋值后,我使用sliverObserverController.animateTo,它的index最多就只能是3,大于3就会失效。请问有什么办法可以解决吗?由于一些依赖问题,我现在用的还是3.10.5,升不了3.13,用不了SliverMainAxisGroup..

@LinXunFeng
Copy link
Member

_sliverListCtx 需要对应起来使用,比如使用 _sliverListCtx1 来记录第一个 SliverList,使用 _sliverListCtx2 来记录第二个 SliverList,然后在使用 sliverObserverController.animateTo 的时候记得传对应的 _sliverListCtx

@Dabbit-Chan
Copy link
Author

哦哦原来如此

@Dabbit-Chan
Copy link
Author

_sliverListCtx 需要对应起来使用,比如使用 _sliverListCtx1 来记录第一个 SliverList,使用 _sliverListCtx2 来记录第二个 SliverList,然后在使用 sliverObserverController.animateTo 的时候记得传对应的 _sliverListCtx

在使用sliverObserverController.jumpTo的时候遇到了一个问题。我有4个sliverlist,list里分别有4,4,1,1个child,每个child的高度都是120,我也已经用了SliverFixedExtentList,jumpTo也已经设置了isFixedHeight: true。刚进入列表时,我可以直接jumpTo第四个sliverCtx,此时再点击jumpTo第一个sliverCtx就会失败,并且没有抛出异常。jumpTo的index都是0。

但是如果我jumpTo第四个,然后再jumpTo第三个,再jumpTo第二个,再jumpTo第一个之后我再jumpTo第四个。此时就可以直接jumpTo第一个了。请问有什么解决方法吗?

@Dabbit-Chan
Copy link
Author

23-08-29-15-11-12.mp4

@Dabbit-Chan
Copy link
Author

补充说明:监听了一下这四个context,发现第一个context的firstChild一直为空,context.renderObject.firstChild == null。按理说firstChild为空不是应该不能跳转吗?为什么视频里后面那几次又可以跳转呢?

@LinXunFeng
Copy link
Member

这个是否可以提供一个可复现的 demo ?

@Dabbit-Chan
Copy link
Author

我试试看,我把SliverFixedExtentList改成SliverList之后就没问题了,只是如果间隔太远的话,jumpTo会卡...
运营一开始跟我说最多也就100多个item,结果现在直接塞了1000多个...

@LinXunFeng
Copy link
Member

产品运营的话都是不能信的 😂

@Dabbit-Chan
Copy link
Author

flutter3.10.5
flutter_sticky_header: ^0.6.5
scrollview_observer: ^1.16.3

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_sticky_header/flutter_sticky_header.dart';
import 'package:scrollview_observer/scrollview_observer.dart';

void main() {
  runApp(const MyApp());
}

class DemoModel {
  final String tag;
  final List<String> value;

  const DemoModel({
    required this.tag,
    required this.value,
  });
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  // 数据
  List<DemoModel> modelList = [];

  // 固定值
  double itemHeight = 120;
  double susHeight = 40;
  final appBarKey = GlobalKey();

  // 滚动相关
  final scrollController = ScrollController();
  late final SliverObserverController sliverObserverController;
  List<BuildContext> contextList = [];

  @override
  void initState() {
    super.initState();
    sliverObserverController = SliverObserverController(controller: scrollController);
    DemoModel model1 = const DemoModel(
      tag: '一',
      value: ['一:0','一:1','一:2','一:3'],
    );
    DemoModel model2 = const DemoModel(
      tag: '二',
      value: ['二:0','二:1','二:2','二:3'],
    );
    DemoModel model3 = const DemoModel(
      tag: '三',
      value: ['三:0'],
    );
    DemoModel model4 = const DemoModel(
      tag: '四',
      value: ['四:0'],
    );
    modelList.addAll([model1, model2, model3, model4]);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SliverViewObserver(
        controller: sliverObserverController,
        sliverContexts: () {
          return contextList;
        },
        child: CustomScrollView(
          controller: scrollController,
          physics: const ClampingScrollPhysics(),
          slivers: [
            SliverAppBar(
              key: appBarKey,
              pinned: true,
              title: Text(widget.title),
            ),
            ...List.generate(modelList.length, (mainIndex) {
              return SliverStickyHeader(
                header: Container(
                  height: susHeight,
                  color: Colors.white,
                  padding: const EdgeInsets.only(left: 12),
                  alignment: Alignment.centerLeft,
                  child: Text(
                    modelList[mainIndex].tag,
                  ),
                ),
                sliver: SliverFixedExtentList(
                  itemExtent: itemHeight,
                  delegate: SliverChildBuilderDelegate(
                        (context, index) {
                      if (contextList.length < mainIndex + 1) {
                        contextList.add(context);
                      }

                      final random = Random(index);

                      return Container(
                        padding: const EdgeInsets.only(left: 12),
                        color: Color.fromRGBO(
                          random.nextInt(255),
                          random.nextInt(255),
                          random.nextInt(255),
                          1,
                        ),
                        alignment: Alignment.centerLeft,
                        child: Text(
                          modelList[mainIndex].value[index],
                        ),
                      );
                    },
                    childCount: modelList[mainIndex].value.length,
                  ),
                ),
              );
            }),
            SliverToBoxAdapter(
              child: SizedBox(
                height: MediaQuery.of(context).size.height - MediaQuery.of(context).padding.vertical - kToolbarHeight - susHeight - modelList.last.value.length * itemHeight - 40,
              ),
            ),
          ],
        ),
      ),
      bottomNavigationBar: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Row(
            children: List.generate(modelList.length, (index) {
              return Expanded(
                child: InkWell(
                  onTap: () {
                    sliverObserverController.jumpTo(
                      sliverContext: contextList[index],
                      index: 0,
                      isFixedHeight: true,
                      offset: (offset) {
                        return ObserverUtils.calcPersistentHeaderExtent(
                          key: appBarKey,
                          offset: offset,
                        );
                      },
                    );
                  },
                  child: Container(
                    alignment: Alignment.center,
                    height: 40,
                    decoration: BoxDecoration(
                      border: Border.all(width: 0.5),
                    ),
                    child: Text(
                      modelList[index].tag,
                    ),
                  ),
                ),
              );
            }),
          ),
          SizedBox(
            height: MediaQuery.of(context).padding.bottom,
          ),
        ],
      ),
    );
  }
}

@Dabbit-Chan
Copy link
Author

一开始点“四”,再点“一”就能复现

@LinXunFeng
Copy link
Member

你的 jumpTo 先用下面这种方式吧,我调整需要花些时间,不会那么快发布新版本

jumpToSliverTop(BuildContext context) async {
  final indexOffsetMap =
      sliverObserverController.indexOffsetMap[context] ?? {};
  if (indexOffsetMap[0] == null) {
    final obj = ObserverUtils.findRenderObject(context);
    if (obj is! RenderSliverMultiBoxAdaptor) return;
    if (obj.firstChild == null) {
      await sliverObserverController.controller?.animateTo(
        obj.constraints.precedingScrollExtent,
        curve: Curves.ease,
        duration: const Duration(milliseconds: 1),
      );
      await SchedulerBinding.instance.endOfFrame;
    }
  }

  sliverObserverController.jumpTo(
    sliverContext: context,
    index: 0,
    isFixedHeight: true,
    offset: (offset) {
      return ObserverUtils.calcPersistentHeaderExtent(
        key: appBarKey,
        offset: offset,
      );
    },
  );
}

使用

jumpToSliverTop(contextList[index]);

@Dabbit-Chan
Copy link
Author

好的我明天试试。这里的意思是先往上滚动到加载出第一个context,然后再jump?

@LinXunFeng
Copy link
Member

是的,间隔多个后目标sliver没有任何子部件被渲染,所以这里先回到目标sliver的头部,待渲染完毕后再做跳转

@Dabbit-Chan
Copy link
Author

完美ok了,感谢。期待大佬调整后的新版本。看大佬的源码,属于是第一次那么深入地去看sliver那堆东西了

@LinXunFeng
Copy link
Member

感谢你的反馈,问题已在 1.16.5 版本中处理 : )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants