簡體   English   中英

如何滾動到 ListView 中的下一項

[英]How to scroll to the next item in the ListView

我正在嘗試為下一個元素制作一個可滾動的 ListView,以便它始終位於頁面的開頭(或中心)(如在 PageView 中)

我的問題是慣性,滾動后元素向后移動。

如何在沒有慣性的情況下實現項目的行為?

代碼:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

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

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  List<ScrollController> _horizontalControllers;

  ScrollController _verticalController;

  var _itemCountHorizontal = 15;

  bool _inProgress;

  Orientation get isPortrait => MediaQuery.of(context).orientation;

  double get _height => MediaQuery.of(context).size.height;
  double get _width => MediaQuery.of(context).size.width;

  double get horizontalPadding {
    double _padd;
    if (isPortrait == Orientation.portrait) {
      _padd = _width * 0.01;
    } else {
      _padd = _width * 0.01;
    }
    return _padd;
  }

  double get verticalPadding {
    double _padd;
    if (isPortrait == Orientation.portrait) {
      _padd = cardHeight * 0.005;
    } else {
      _padd = (_height - cardHeight) / 2;
    }
    return _padd;
  }

  double get cardHeight {
    double cardH;
    if (isPortrait == Orientation.portrait) {
      cardH = cardWidth * 1.7;
    } else {
      cardH = _height * 0.9;
    }
    return cardH;
  }

  double get cardWidth {
    var cardW = _width * 0.99;
    if (cardW > _height / 1.7) {
      cardW = _height / 1.77;
    }
    return cardW;
  }

  @override
  void initState() {
    _horizontalControllers = [
      ScrollController(),
      ScrollController(),
      ScrollController(),
      ScrollController(),
      ScrollController(),
    ];
    _verticalController = ScrollController();
    _inProgress = false;
    super.initState();
  }

  @override
  void dispose() {
    _horizontalControllers.forEach((element) {
      element.dispose();
    });
    _verticalController.dispose();
    super.dispose();
  }

  void _onEndScrollVertical(ScrollMetrics metrics) {
    print("scroll before = ${metrics.extentBefore}");
    print("scroll after = ${metrics.extentAfter}");
    print("scroll inside = ${metrics.extentInside}");
    print("index = ${metrics.axisDirection}");
    print("item HEIGHT => $cardHeight");
    final topPadd = MediaQuery.of(context).padding.top;
    print('TOPPPPPPPPPP $topPadd');

    /*  int point = metrics.extentAfter ~/ (_height - topPadd);

    var offset = (_height - topPadd) * point;
    _inProgress = true;
    Future.delayed(Duration(milliseconds: 100), () {
      _verticalController.animateTo(offset,
          duration: Duration(milliseconds: 1000), curve: Curves.fastOutSlowIn);
    });
    _inProgress = false;
    */

    var halfOfTheHeight = cardHeight / 2;
    var offsetOfItem = metrics.extentBefore % cardHeight;
    if (offsetOfItem < halfOfTheHeight) {
      final offset = metrics.extentBefore - offsetOfItem;
      print("offsetOfItem1 = $offsetOfItem offset = $offset");
      Future.delayed(Duration(milliseconds: 50), () {
        _verticalController.animateTo(offset,
            duration: Duration(milliseconds: 1000),
            curve: Curves.fastOutSlowIn);
      });
    } else if (offsetOfItem > halfOfTheHeight) {
      final offset = metrics.extentBefore + offsetOfItem;
      print("offsetOfItem2 = $offsetOfItem offset = $offset");
      Future.delayed(Duration(milliseconds: 50), () {
        _verticalController.animateTo(offset,
            duration: Duration(milliseconds: 1000),
            curve: Curves.fastOutSlowIn);
      });
    }
  }

  void _onEndScrollHorizontal(ScrollMetrics metrics, int index) {
    print("scroll before = ${metrics.extentBefore}");
    print("scroll after = ${metrics.extentAfter}");
    print("scroll inside = ${metrics.extentInside}");
    print("item WIDTH => $cardWidth");

    var halfOfTheWidth = _width / 2;
    var offsetOfItem = metrics.extentBefore % _width;
    if (offsetOfItem < halfOfTheWidth) {
      final offset = metrics.extentBefore - offsetOfItem;
      print("offsetOfItem1 = $offsetOfItem offset = $offset");
      _inProgress = true;
      Future.delayed(Duration(milliseconds: 10), () {
        _horizontalControllers[index].animateTo(offset,
            duration: Duration(milliseconds: 500), curve: Curves.fastOutSlowIn);
      });
      _inProgress = false;
    } else if (offsetOfItem > halfOfTheWidth) {
      _inProgress = true;
      final offset = metrics.extentBefore + offsetOfItem;
      print("offsetOfItem2 = $offsetOfItem offset = $offset");
      Future.delayed(Duration(milliseconds: 10), () {
        _horizontalControllers[index].animateTo(offset,
            duration: Duration(milliseconds: 500), curve: Curves.fastOutSlowIn);
      });
      _inProgress = false;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: NotificationListener<ScrollNotification>(
          onNotification: (scrollNotification) {
            if (scrollNotification is ScrollEndNotification &&
                scrollNotification.depth == 0) {
              if (!_inProgress) {
                print('ScrollEndNotification ===> $scrollNotification');
                _onEndScrollVertical(scrollNotification.metrics);
              }
            }
            return null;
          },
          child: buildListViewVertical(),
        ),
      ),
    );
  }

  Widget buildListViewVertical() {
    return ListView.builder(
      itemCount: _itemCountHorizontal,
      itemExtent: cardHeight,
      controller: _verticalController,
      itemBuilder: (BuildContext context, int index) {
        return NotificationListener<ScrollNotification>(
            onNotification: (scrollNotification) {
              if (scrollNotification is ScrollEndNotification &&
                  scrollNotification.depth == 0) {
                print('ScrollEndNotification ===> $scrollNotification');
                _onEndScrollHorizontal(scrollNotification.metrics, index);
              }
              return null;
            },
            child: buildListViewHorizontal(index));
      },
    );
  }

  Widget buildListViewHorizontal(int index) {
    return ListView.builder(
      controller: _horizontalControllers[index],
      physics: ClampingScrollPhysics(),
      shrinkWrap: true,
      scrollDirection: Axis.horizontal,
      itemCount: _itemCountHorizontal + 1,
      itemBuilder: (BuildContext context, int index) =>
          index < _itemCountHorizontal
              ? Padding(
                  padding: EdgeInsets.only(
                    left: horizontalPadding,
                    right: horizontalPadding,
                    top: verticalPadding,
                    bottom: verticalPadding,
                  ),
                  child: Container(height: 340, width: 200, color: Colors.red),
                )
              : SizedBox(
                  width: 50,
                ),
    );
  }
}

這是 DartPad 的一個工作示例

更新:

我將 CustomScrollPhysics() 添加到 ListView,該解決方案消除了反向運動的慣性。 然而,從索引 0 及以上移動時,慣性仍然存在......代碼:

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

class CustomScrollPhysics extends ScrollPhysics {
  const CustomScrollPhysics({ScrollPhysics parent}) : super(parent: parent);

  @override
  SpringDescription get spring => SpringDescription(damping: 0.1);

  @override
  CustomScrollPhysics applyTo(ScrollPhysics ancestor) {
    return CustomScrollPhysics(parent: buildParent(ancestor));
  }
}

您可以使用 DraggableScrollableSheet 和 SingleChildScrollView 小部件

首先,您需要將小部件更改為 statefullwidget,然后您需要使用 Global 鍵滾動到您想要的位置。 以及您要滾動到“小部件前”的位置。 必須有名字。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM