如何创建与颤动中的小部件滚动同步的标签栏?

如何解决如何创建与颤动中的小部件滚动同步的标签栏?

基本上我想在这里完成的是有一个横向的类别列表(可以是 TabBar)和一个纵向的类别列表,每个类别中都有项目列表。

当您点击一个类别时,它应该滚动到类别垂直列表中的某个位置。 并且当您滚动垂直列表时,顶部的活动类别也应更新当前的活动类别。

我在 FoodPanda 应用程序中看到了这个功能,但我无法复制它。

有关如何完成此功能的任何建议?

这是用户界面的屏幕截图。

enter image description here

解决方法

您可以在下面复制粘贴运行完整代码
您可以使用包 https://pub.dev/packages/scrollable_list_tabview
代码片段

body: ScrollableListTabView(
        tabHeight: 48,bodyAnimationDuration: const Duration(milliseconds: 150),tabAnimationCurve: Curves.easeOut,tabAnimationDuration: const Duration(milliseconds: 200),tabs: [
          ScrollableListTab(
              tab: ListTab(
                  label: Text('Vegetables'),icon: Icon(Icons.group),showIconOnList: false),body: ListView.builder(
                shrinkWrap: true,physics: NeverScrollableScrollPhysics(),itemCount: 10,itemBuilder: (_,index) => ListTile(
                  leading: Container(
                    height: 40,width: 40,decoration: BoxDecoration(
                        shape: BoxShape.circle,color: Colors.grey),alignment: Alignment.center,child: Text(index.toString()),),title: Text('Vegetables element $index'),)),ScrollableListTab(
              tab: ListTab(label: Text('Fruits'),icon: Icon(Icons.add)),

工作演示

enter image description here

完整代码

import 'package:flutter/material.dart';
import 'package:scrollable_list_tabview/scrollable_list_tabview.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',debugShowCheckedModeBanner: false,theme: ThemeData(
        primarySwatch: Colors.blue,visualDensity: VisualDensity.adaptivePlatformDensity,home: MyHomePage(title: 'Flutter ScrollableListTabView Example'),);
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key,this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),body: ScrollableListTabView(
        tabHeight: 48,title: Text('Fruits element $index'),ScrollableListTab(
              tab: ListTab(label: Text('Meat'),icon: Icon(Icons.group)),title: Text('Meat element $index'),ScrollableListTab(
              tab: ListTab(
                  label: Text('Herbs&Spices'),icon: Icon(Icons.subject)),body: GridView.builder(
                shrinkWrap: true,gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: 2),index) => Card(
                  child: Center(child: Text('Herbs&Spices element $index')),ScrollableListTab(
              tab: ListTab(
                  label: Text('Egg'),icon: Icon(Icons.subject),showIconOnList: true),index) => Card(
                  child: Center(child: Text('Egg element $index')),],);
  }
}
,

我尝试了多种方法,但这似乎最适合我。

包:

scroll_to_index: ^2.0.0 
rect_getter: ^1.0.0

rect_getter 的使用灵感来自:https://gist.github.com/debuggerx01/49f108d68ed903458e9478b4f0c186f4

代码(Food Panda Clone):

import 'package:flutter/material.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
import 'package:food_panda_sticky_header/colors.dart';
import 'package:food_panda_sticky_header/example_data.dart';
import 'package:food_panda_sticky_header/widgets/widgets.dart';
import 'package:rect_getter/rect_getter.dart';

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateMixin {
  bool isCollapsed = false;
  late AutoScrollController scrollController;
  late TabController tabController;

  final double expandedHeight = 500.0;
  final PageData data = ExampleData.data;
  final double collapsedHeight = kToolbarHeight;

  final listViewKey = RectGetter.createGlobalKey();
  Map<int,dynamic> itemKeys = {};

  // prevent animate when press on tab bar
  bool pauseRectGetterIndex = false;

  @override
  void initState() {
    tabController = TabController(length: data.categories.length,vsync: this);
    scrollController = AutoScrollController();
    super.initState();
  }

  @override
  void dispose() {
    scrollController.dispose();
    tabController.dispose();
    super.dispose();
  }

  List<int> getVisibleItemsIndex() {
    Rect? rect = RectGetter.getRectFromKey(listViewKey);
    List<int> items = [];
    if (rect == null) return items;
    itemKeys.forEach((index,key) {
      Rect? itemRect = RectGetter.getRectFromKey(key);
      if (itemRect == null) return;
      if (itemRect.top > rect.bottom) return;
      if (itemRect.bottom < rect.top) return;
      items.add(index);
    });
    return items;
  }

  void onCollapsed(bool value) {
    if (this.isCollapsed == value) return;
    setState(() => this.isCollapsed = value);
  }

  bool onScrollNotification(ScrollNotification notification) {
    if (pauseRectGetterIndex) return false;
    int lastTabIndex = tabController.length - 1;
    List<int> visibleItems = getVisibleItemsIndex();

    bool reachLastTabIndex = visibleItems.length <= 2 && visibleItems.last == lastTabIndex;
    if (reachLastTabIndex) {
      tabController.animateTo(lastTabIndex);
    } else {
      int sumIndex = visibleItems.reduce((value,element) => value + element);
      int middleIndex = sumIndex ~/ visibleItems.length;
      if (tabController.index != middleIndex) tabController.animateTo(middleIndex);
    }

    return false;
  }

  void animateAndScrollTo(int index) {
    pauseRectGetterIndex = true;
    tabController.animateTo(index);
    scrollController
        .scrollToIndex(index,preferPosition: AutoScrollPosition.begin)
        .then((value) => pauseRectGetterIndex = false);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      extendBodyBehindAppBar: true,backgroundColor: scheme.background,body: RectGetter(
        key: listViewKey,child: NotificationListener<ScrollNotification>(
          child: buildSliverScrollView(),onNotification: onScrollNotification,);
  }

  Widget buildSliverScrollView() {
    return CustomScrollView(
      physics: const ClampingScrollPhysics(),controller: scrollController,slivers: [
        buildAppBar(),buildBody(),);
  }

  SliverAppBar buildAppBar() {
    return FAppBar(
      data: data,context: context,scrollController: scrollController,expandedHeight: expandedHeight,collapsedHeight: collapsedHeight,isCollapsed: isCollapsed,onCollapsed: onCollapsed,tabController: tabController,onTap: (index) => animateAndScrollTo(index),);
  }

  SliverList buildBody() {
    return SliverList(
      delegate: SliverChildListDelegate(
        List.generate(data.categories.length,(index) {
          itemKeys[index] = RectGetter.createGlobalKey();
          return buildCategoryItem(index);
        }),);
  }

  Widget buildCategoryItem(int index) {
    Category category = data.categories[index];
    return RectGetter(
      key: itemKeys[index],child: AutoScrollTag(
        key: ValueKey(index),index: index,child: CategorySection(category: category),);
  }
}

演示https://github.com/theacheng/food_panda_sticky_header/pull/4

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?