Flutter 如何在颤振中使水平菜单与垂直产品列表自动滚动
我在页面顶部有一个水平分类菜单和一个长长的垂直滚动产品列表。目前我正在寻找的是自动化的水平菜单上的页面顶部,只要我去到各自的产品 因此,基本上我计划在顶部水平菜单中包含项目类别,所有包含这些类别的产品作为其结构的一部分,放在一个长列表中。因此,一旦用户(垂直)浏览这些产品,水平菜单中的类别就会突出显示,但一旦用户通过向下浏览项目列表转到产品的另一部分,则顶部水平菜单上的类别就会改变Flutter 如何在颤振中使水平菜单与垂直产品列表自动滚动,flutter,flutter-layout,flutter-dependencies,flutter-animation,Flutter,Flutter Layout,Flutter Dependencies,Flutter Animation,我在页面顶部有一个水平分类菜单和一个长长的垂直滚动产品列表。目前我正在寻找的是自动化的水平菜单上的页面顶部,只要我去到各自的产品 因此,基本上我计划在顶部水平菜单中包含项目类别,所有包含这些类别的产品作为其结构的一部分,放在一个长列表中。因此,一旦用户(垂直)浏览这些产品,水平菜单中的类别就会突出显示,但一旦用户通过向下浏览项目列表转到产品的另一部分,则顶部水平菜单上的类别就会改变 Categ 1 Categ 2 Categ 3 Categ 4 -------------> Produc
Categ 1 Categ 2 Categ 3 Categ 4 ------------->
Product 1
Product 2
Product 3
Product 4
我需要帮助才能轻而易举地做到这一点
我知道选项卡栏和嵌套滚动视图,但这不是我想要的,我需要它们位于同一页面上,并且我不想根据选项卡的不同来区分产品。我希望在一个大列表中的同一页上的所有产品,只希望自动突出显示类别(取决于您是哪种产品)。我还需要帮助滚动产品到各自的位置,只要我点击该类别
感谢您的帮助您可以设置类别和产品列表的
项目范围
,并使用它计算滚动偏移量以检测当前产品列表项目
样本
import 'dart:math';
import 'package:flutter/material.dart';
Future<void> main() async {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final Map<String, List<String>> _categories = <String, List<String>>{};
final List<int> _categoryOffsets = <int>[];
final ScrollController _categoriesController = ScrollController();
final ScrollController _productsController = ScrollController();
final int _categoryItemExtent = 150;
final int _productItemExtent = 100;
int _categoryIdx = 0;
@override
void initState() {
super.initState();
_productsController.addListener(_onProductsScroll);
for (int i = 1; i <= 10; i++) {
int productCnt = Random().nextInt(10);
productCnt = productCnt == 0 ? 1 : productCnt;
_categories['Category $i'] =
List.generate(productCnt, (int j) => 'Product ${i * 10 + j}');
final int prevOffset = i == 1 ? 0 : _categoryOffsets[i - 2];
_categoryOffsets.add(prevOffset + (_productItemExtent * productCnt));
}
}
@override
Widget build(BuildContext context) {
final List<String> allProducts = _categories.entries.fold<List<String>>(
<String>[],
(previousValue, element) {
previousValue.addAll(element.value);
return previousValue;
},
);
return Scaffold(
body: Column(
children: <Widget>[
Expanded(
flex: 1,
child: ListView.builder(
controller: _categoriesController,
scrollDirection: Axis.horizontal,
itemExtent: _categoryItemExtent.toDouble(),
itemCount: _categories.length,
itemBuilder: (_, int i) => Card(
color: _categoryIdx == i ? Colors.green : null,
child: Text(_categories.keys.elementAt(i)),
),
),
),
Expanded(
flex: 3,
child: ListView.builder(
controller: _productsController,
itemExtent: _productItemExtent.toDouble(),
itemCount: allProducts.length,
itemBuilder: (_, int i) {
return Card(
child: Text(allProducts[i]),
);
},
),
),
],
),
);
}
void _onProductsScroll() {
final double offset = _productsController.offset;
for (int i = 0; i < _categoryOffsets.length; i++) {
if (offset <= _categoryOffsets[i]) {
if (_categoryIdx != i) {
print('Scroll to category $i');
_categoriesController.animateTo(
(i * _categoryItemExtent).toDouble(),
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
setState(() => _categoryIdx = i);
}
break;
}
}
}
@override
void dispose() {
_categoriesController.dispose();
_productsController.removeListener(_onProductsScroll);
_productsController.dispose();
super.dispose();
}
}
import'dart:math';
进口“包装:颤振/材料.省道”;
Future main()异步{
runApp(MyApp());
}
类MyApp扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回材料PP(
debugShowCheckedModeBanner:false,
主页:主页(),
);
}
}
类主页扩展了StatefulWidget{
@凌驾
_HomePageState createState()=>\u HomePageState();
}
类_HomePageState扩展状态{
最终地图_分类={};
最终列表_categoryoffset=[];
最终ScrollController_categoriesController=ScrollController();
最终ScrollController_productsController=ScrollController();
最终int_分类项目范围=150;
最终整数_productItemExtent=100;
int _categoryIdx=0;
@凌驾
void initState(){
super.initState();
_productsController.addListener(\u onProductsScroll);
对于(inti=1;i'乘积${i*10+j}');
最终int prevOffset=i==1?0:_categoryOffset[i-2];
_添加(prevOffset+(_productItemExtent*productCnt));
}
}
@凌驾
小部件构建(构建上下文){
最终列表所有产品=_categories.entries.fold(
[],
(以前的值,元素){
previousValue.addAll(element.value);
返回以前的值;
},
);
返回脚手架(
正文:专栏(
儿童:[
扩大(
弹性:1,
子项:ListView.builder(
控制器:_分类控制器,
滚动方向:轴水平,
itemExtent:\u categoryItemExtent.toDouble(),
itemCount:_categories.length,
itemBuilder:(_,int i)=>卡(
颜色:_categoryIdx==i?颜色。绿色:null,
子:文本(_categories.keys.elementAt(i)),
),
),
),
扩大(
弹性:3,
子项:ListView.builder(
控制器:\产品控制器,
itemExtent:\u productItemExtent.toDouble(),
itemCount:allProducts.length,
itemBuilder:(u,int i){
回程卡(
子项:文本(所有产品[i]),
);
},
),
),
],
),
);
}
void _onProductsScroll(){
最终双偏移=_productsController.offset;
对于(int i=0;i<_categoryOffsets.length;i++){
if(偏移量_categoryIdx=i);
}
打破
}
}
}
@凌驾
无效处置(){
_categoriesController.dispose();
_productsController.removeListener(_onProductsScroll);
_productsController.dispose();
super.dispose();
}
}
哇,这正是我想要的。非常感谢!祝你好运