如何在 flutter 中使用垂直产品列表使水平菜单自动滚动

how to make horizontal menu auto scroll with vertical product list in flutter

我在页面顶部有一个水平类别菜单和一个长的可垂直滚动的产品项目列表。目前我正在寻找的是在我进入相应产品后立即自动化页面顶部的水平菜单。

所以基本上我计划在顶部水平菜单中包含项目类别,并将包含这些类别的所有产品作为其结构的一部分放在一个长列表中。因此,一旦用户(垂直)浏览这些产品,水平菜单中的类别就会突出显示,但一旦他通过向下浏览项目列表进入产品的另一部分,顶部水平菜单中的类别就会发生变化。

Categ 1 Categ 2 Categ 3 Categ 4 ------------->

Product 1
Product 2
Product 3
Product 4

我需要帮助来实现它。

知道选项卡栏和嵌套滚动视图但这不是我想要的我需要它们在同一页面上但我不想根据标签区分产品。我希望所有产品都在一个大列表中的同一页面上,并且只希望自动突出显示类别(取决于您是哪种产品)。单击类别后,我还需要帮助将产品滚动到相应的位置。

谢谢你帮助我

您可以设置类别和产品列表的itemExtent,并使用它来计算滚动偏移量以检测哪个是当前产品列表项。

示例...

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();
  }
}