如何解决如何创建与颤动中的小部件滚动同步的标签栏?
基本上我想在这里完成的是有一个横向的类别列表(可以是 TabBar)和一个纵向的类别列表,每个类别中都有项目列表。
当您点击一个类别时,它应该滚动到类别垂直列表中的某个位置。 并且当您滚动垂直列表时,顶部的活动类别也应更新当前的活动类别。
我在 FoodPanda 应用程序中看到了这个功能,但我无法复制它。
有关如何完成此功能的任何建议?
这是用户界面的屏幕截图。
解决方法
您可以在下面复制粘贴运行完整代码
您可以使用包 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)),
工作演示
完整代码
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 举报,一经查实,本站将立刻删除。