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

[Feature request] 能否通过这个组件做个一类似美团外卖店铺首页或饿了么店铺首页的示例呢? #88

Open
sjkyyt01 opened this issue Aug 17, 2024 · 1 comment
Assignees

Comments

@sjkyyt01
Copy link

Platforms

dart

Description

我并不是一个资深的Flutter人员,目前在研究你的这个插件,想结合你这个做一个类似美团外卖或是饿了么店铺首页页面,通过你的示例进行结合,要么商品列表上移时,SliverAppBar无法上移,要么上移后会被SliverAppBar或是SliverPersistentHeader总价盖住,能否结合这些做一个类似美团外卖或是饿了么店铺首页的示例呢?

Why

No response

@sjkyyt01
Copy link
Author

sjkyyt01 commented Aug 17, 2024

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_sticky_header/flutter_sticky_header.dart';
import 'package:sliver_tools/sliver_tools.dart';

import '../../components/scrollview_observer/src/common/observer_typedef.dart';
import '../../components/scrollview_observer/src/sliver/sliver_observer_controller.dart';
import '../../components/scrollview_observer/src/sliver/sliver_observer_view.dart';
import '../../components/scrollview_observer/src/utils/src/nested_scroll_util.dart';
import '../../components/scrollview_observer/src/utils/src/observer_utils.dart';
import '../../components/scrollview_observer/src/utils/src/slivers.dart';
import '../demo/common/wigdets.dart';

class Category {
  int id;
  String categoryName;
  List<Goods> goods;

  Category({
    this.id = 0,
    this.categoryName = '',
    this.goods = const [],
  });
}

class Goods {
  int id;
  String goodsName;

  Goods({
    this.id = 0,
    this.goodsName = '',
  });
}


class Other4Page extends StatefulWidget {
  const Other4Page({super.key});

  @override
  State<Other4Page> createState() => _Other4PageState();
}

class _Other4PageState extends State<Other4Page> with TickerProviderStateMixin {
  List<Category> list = List.generate(10, (index) {
    int categoryId = index + 1;
    return Category(
      id: categoryId,
      categoryName: 'category_$categoryId',
      goods: List.generate(10, (i) => Goods(id: i + categoryId * 10, goodsName: 'category_$categoryId - item_${i + categoryId * 10}')),
    );
  });

  final nestedScrollViewKey = GlobalKey();
  final appBarKey = GlobalKey();
  final appHeaderKey = GlobalKey();
  final tabBarKey = GlobalKey();

  final scrollController = ScrollController();
  late SliverObserverController sliverItemObserverController;
  final nestedScrollUtil = NestedScrollUtil();

  // late final SliverObserverController sliverObserverController;
  Map<int, BuildContext> itemSliverIndexCtxMap = {};
  Map<int, BuildContext> sliverIndexCtxMap = {};

  ValueNotifier<int> tabCurrentSelectedIndex = ValueNotifier(0);
  bool isIgnoreCalcTabBarIndex = false;

  late TabController tabBarController;

  @override
  void initState() {
    super.initState();
    tabBarController = TabController(
      length: 2,
      vsync: this,
    );
    sliverItemObserverController = SliverObserverController(controller: scrollController);
    nestedScrollUtil.outerScrollController = scrollController;
    // sliverObserverController = SliverObserverController(
    //   controller: scrollController,
    // );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SliverViewObserver(
        /// To observe which sliver is currently the first.
        sliverContexts: () => sliverIndexCtxMap.values.toList(),
        // customOverlap: (sliverContext) {
        //   return nestedScrollUtil.calcOverlap(
        //     nestedScrollViewKey: nestedScrollViewKey,
        //     sliverContext: sliverContext,
        //   );
        // },
        triggerOnObserveType: ObserverTriggerOnObserveType.directly,
        dynamicLeadingOffset: () {
          // Accumulate the height of all PersistentHeader.
          return ObserverUtils.calcPersistentHeaderExtent(
                key: appBarKey,
                offset: scrollController.offset,
              ) +
              1;
          // To avoid tabBar index rebound.
        },
        onObserveViewport: (result) {
          if (isIgnoreCalcTabBarIndex) return;
          int? currentTabIndex;
          final currentFirstSliverCtx = result.firstChild.sliverContext;
          for (var sectionIndex in sliverIndexCtxMap.keys) {
            final ctx = sliverIndexCtxMap[sectionIndex];
            if (ctx == null) continue;
            // If they are not the same sliver, continue.
            if (currentFirstSliverCtx != ctx) continue;
            // If the sliver is not visible, continue.
            final visible = (ctx.findRenderObject() as RenderSliver).geometry?.visible ?? false;
            if (!visible) continue;
            currentTabIndex = sectionIndex;
            break;
          }
          if (currentTabIndex == null) return;
          updateTabBarIndex(currentTabIndex);
        },
        child: SliverViewObserver(
          /// To observe sliver items and handle scrollTo.
          controller: sliverItemObserverController,
          sliverContexts: () => itemSliverIndexCtxMap.values.toList(),
          child: NestedScrollView(
            // key: nestedScrollViewKey,
            controller: scrollController,
            headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
              return [
                SliverOverlapAbsorber(
                  handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                  sliver: MultiSliver(
                    // key: nestedScrollViewKey,
                    pushPinnedChildren: true,
                    children: [
                      SliverAppBar(
                        key: appBarKey,
                        forceElevated: innerBoxIsScrolled,
                        pinned: true,
                        title: const Text('Other4Page'),
                      ),
                      SliverToBoxAdapter(
                        key: appHeaderKey,
                        child: Container(
                          height: 100,
                          color: Colors.red[200],
                        ),
                      ),
                      SliverPersistentHeader(
                        key: tabBarKey,
                        pinned: true,
                        delegate: SliverHeaderDelegate.fixedHeight(
                          height: 40,
                          child: Container(
                            color: Colors.blue,
                            child: TabBar(
                              controller: tabBarController,
                              tabs: const [
                                Tab(text: "Tab 1"),
                                Tab(text: "Tab 2"),
                              ],
                            ),
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ];
            },
            body: Builder(
              builder: (context) {
                return TabBarView(
                  controller: tabBarController,
                  children: [
                    Row(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        SizedBox(
                          width: 100,
                          child: Container(
                            height: 500,
                            color: Colors.blue[200],
                            child: const Text('data'),
                          ),
                        ),
                        Expanded(
                          child: CustomScrollView(
                            // controller: sliverItemObserverController.controller,
                            // physics: const RangeMaintainingScrollPhysics(),
                            physics: const ClampingScrollPhysics(),
                            slivers: [
                              SliverOverlapInjector(
                                handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                              ),
                              ...List.generate(list.length, (mainIndex) {
                                return _buildSectionListView(mainIndex);
                              }),
                            ],
                          ),
                        ),
                      ],
                    ),
                    Container(
                      height: 500,
                      color: Colors.white,
                    ),
                  ],
                );
              }
            ),
          ),
        ),
      ),
      bottomNavigationBar: buildBottomNavigationBar(context),
    );
  }

  Widget buildBottomNavigationBar(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        Row(
          children: List.generate(list.length, (index) {
            return Expanded(
              child: InkWell(
                onTap: () async {
                  updateTabBarIndex(index);
                  isIgnoreCalcTabBarIndex = true;
                  // await sliverItemObserverController.jumpTo(
                  //   sliverContext: itemSliverIndexCtxMap[index],
                  //   index: 0,
                  //   isFixedHeight: true,
                  //   offset: (offset) {
                  //     return ObserverUtils.calcPersistentHeaderExtent(
                  //       key: appBarKey,
                  //       offset: offset,
                  //     );
                  //   },
                  // );
                  await sliverItemObserverController.animateTo(
                    sliverContext: itemSliverIndexCtxMap[index],
                    index: 0,
                    isFixedHeight: true,
                    duration: const Duration(milliseconds: 200),
                    curve: Curves.easeInOut,
                    offset: (offset) {
                      return ObserverUtils.calcPersistentHeaderExtent(
                        key: appBarKey,
                        offset: offset,
                      );
                    },
                  );
                  isIgnoreCalcTabBarIndex = false;
                },
                child: ValueListenableBuilder(
                  valueListenable: tabCurrentSelectedIndex,
                  builder: (BuildContext context, int value, Widget? child) {
                    return Container(
                      alignment: Alignment.center,
                      height: 40,
                      decoration: BoxDecoration(
                        border: Border.all(width: 0.5),
                        color: value == index ? Colors.amber : Colors.white,
                      ),
                      child: Text(
                        list[index].categoryName,
                      ),
                    );
                  },
                ),
              ),
            );
          }),
        ),
        SizedBox(height: MediaQuery.paddingOf(context).bottom),
      ],
    );
  }

  Widget _buildSectionListView(int mainIndex) {
    return SliverObserveContext(
      child: SliverStickyHeader(
        header: Container(
          color: Colors.red,
          height: 40,
          alignment: Alignment.centerLeft,
          child: Text(
            list[mainIndex].categoryName,
            style: const TextStyle(color: Colors.white, fontSize: 16),
          ),
        ),
        sliver: SliverFixedExtentList(
          itemExtent: 120,
          delegate: SliverChildBuilderDelegate(
            (context, index) {
              // Save the context of SliverList.
              itemSliverIndexCtxMap[mainIndex] = context;
              return Container(
                color: Colors.white,
                margin: const EdgeInsets.symmetric(vertical: 5),
                height: 100,
                child: Text(list[mainIndex].goods[index].goodsName),
              );
            },
            childCount: list[mainIndex].goods.length,
          ),
        ),
      ),
      onObserve: (context) {
        // Save the context of the outermost sliver.
        sliverIndexCtxMap[mainIndex] = context;
      },
    );
  }

  updateTabBarIndex(int index) {
    if (index == tabCurrentSelectedIndex.value) return;
    tabCurrentSelectedIndex.value = index;
  }
}

上面这段代码商品列表在上移后会被挡住,不知道怎么改写

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