如何在列表视图中自动校正滚动位置?(flutter)

fruv7luv  于 2023-03-19  发布在  Flutter
关注(0)|答案(2)|浏览(155)

我有一个水平的列表视图构建器,如上图所示:

有没有一种方法可以自动校正当前位置,使右上角的小部件完全适合他的整个宽度?(这里是数字27)因为如果你停止滚动,它看起来不太好,如果它是像图片中的数字27剪切。所以有没有可能只停止在最完整的itemwidget,使当前位置将不会像在图片中?
下面是我的列表视图生成器代码:

import './date_widget.dart';


import 'package:date_picker_timeline/gestures/tap.dart';
import 'package:flutter/material.dart';
import 'package:intl/date_symbol_data_local.dart';

class MyDatePickerTimeline extends StatefulWidget {
 

  
  
  DateTime currentDate;
  DateChangeListener onDateChange;
 
  String locale;

  // Creates the DatePickerTimeline Widget
  MyDatePickerTimeline(
    this.currentDate, {
    Key key,
    
   
    
    
    
    this.onDateChange,
    this.locale = "de",
  }) : super(key: key);

  @override
  State<StatefulWidget> createState() => new _MyDatePickerTimelineState();
}

class _MyDatePickerTimelineState extends State<MyDatePickerTimeline> {

  @override void initState() {
    super.initState();

    initializeDateFormatting(widget.locale, null);
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      
     // padding: EdgeInsets.only(bottom: 600),
      width: MediaQuery.of(context).size.width ,
      height: 200,
      child: ListView.builder(
        reverse: true,
        itemCount: 5000,
        scrollDirection: Axis.horizontal,
        itemBuilder: (context, index) {
          // Return the Date Widget
          DateTime _date = DateTime.now().subtract(Duration(days: index));
          DateTime date = new DateTime(_date.year, _date.month, _date.day);
          bool isSelected = compareDate(date, widget.currentDate);

          return MyDateWidget(
            date: date,
           
            locale: widget.locale,
            selectionColor:
                isSelected ? Colors.black12 : Colors.transparent,
            onDateSelected: (selectedDate) {
              // A date is selected
              if (widget.onDateChange != null) {
                widget.onDateChange(selectedDate);
              }
              setState(() {
                widget.currentDate = selectedDate;
              });
            },
          );
        },
      ),
    );
  }

  bool compareDate(DateTime date1, DateTime date2) {
    return date1.day == date2.day &&
        date1.month == date2.month &&
        date1.year == date2.year;
  }
}

或者有没有可能设置一个固定的宽度,你可以滚动?

mm5n2pyu

mm5n2pyu1#

您似乎正在查找PageScrollPhysics。请参阅PageScrollPhysics类文档:
页面视图使用的滚动物理特性。这些物理特性使页面视图与页面边界对齐。
ListViewphysics属性设置为PageScrollPhysics将使列表以分页的离散方式滚动。如果您还将ListView中的小部件宽度设置为屏幕宽度的一小部分,则ListView中的小部件将永远不会被剪切,无论列表中有多少项,屏幕变得多大,或者用户如何滚动。
看看我写的这个示例,它向你展示了一种实现这种滚动物理的方法。你可以复制它并在DartPad中运行它,看看它是否是你所寻找的。注意,每页滚动3个条目,而在ListView中总共有7个条目,并且没有办法在视图中剪切它们中的任何一个。

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> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: ListView(
        physics: PageScrollPhysics(),
        scrollDirection: Axis.horizontal,
        children: <Widget>[
          Container(
            width: MediaQuery.of(context).size.width/3,
            color: Colors.amber[900],
            child: const Center(child: Text('Entry A')),
          ),
          Container(
            width: MediaQuery.of(context).size.width/3,
            color: Colors.amber[800],
            child: const Center(child: Text('Entry B')),
          ),
          Container(
            width: MediaQuery.of(context).size.width/3,
            color: Colors.amber[700],
            child: const Center(child: Text('Entry C')),
          ),
          Container(
            width: MediaQuery.of(context).size.width/3,
            color: Colors.amber[600],
            child: const Center(child: Text('Entry D')),
          ),
          Container(
            width: MediaQuery.of(context).size.width/3,
            color: Colors.amber[500],
            child: const Center(child: Text('Entry E')),
          ),
          Container(
            width: MediaQuery.of(context).size.width/3,
            color: Colors.amber[400],
            child: const Center(child: Text('Entry F')),
          ),
          Container(
            width: MediaQuery.of(context).size.width/3,
            color: Colors.amber[300],
            child: const Center(child: Text('Entry G')),
          ),
        ],
      ),
    );
  }
}
gwbalxhn

gwbalxhn2#

可接受的答案很好用,但有一个限制:它根据视口大小进行捕捉。但是,如果你想捕捉列表中的每个项目,这是不可能的。我使用了下面的物理实现,其中捕捉大小可以配置为项目的大小。这有一个不同的限制:它只对同样大小的孩子有效。

import 'package:flutter/material.dart';

class SnapScrollPhysics extends ScrollPhysics {
  const SnapScrollPhysics({super.parent, required this.snapSize});

  final double snapSize;

  @override
  SnapScrollSize applyTo(ScrollPhysics? ancestor) {
    return SnapScrollSize(parent: buildParent(ancestor), snapSize: snapSize);
  }

  double _getPage(ScrollMetrics position) {
    return position.pixels / snapSize;
  }

  double _getPixels(ScrollMetrics position, double page) {
    return page * snapSize;
  }

  double _getTargetPixels(
      ScrollMetrics position, Tolerance tolerance, double velocity) {
    double page = _getPage(position);
    if (velocity < -tolerance.velocity) {
      page -= 0.5;
    } else if (velocity > tolerance.velocity) {
      page += 0.5;
    }
    return _getPixels(position, page.roundToDouble());
  }

  @override
  Simulation? createBallisticSimulation(
      ScrollMetrics position, double velocity) {
    // If we're out of range and not headed back in range, defer to the parent
    // ballistics, which should put us back in range at a page boundary.
    if ((velocity <= 0.0 && position.pixels <= position.minScrollExtent) ||
        (velocity >= 0.0 && position.pixels >= position.maxScrollExtent)) {
      return super.createBallisticSimulation(position, velocity);
    }
    final Tolerance tolerance = this.tolerance;
    final double target = _getTargetPixels(position, tolerance, velocity);
    if (target != position.pixels) {
      return ScrollSpringSimulation(spring, position.pixels, target, velocity,
          tolerance: tolerance);
    }
    return null;
  }

  @override
  bool get allowImplicitScrolling => false;
}

你可以在这个已接受答案的改编例子中看到它的实际作用:

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, required 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: ListView(
        physics: SnapScrollPhysics(snapSize: MediaQuery.of(context).size.width/3),
        scrollDirection: Axis.horizontal,
        children: <Widget>[
          Container(
            width: MediaQuery.of(context).size.width/3,
            color: Colors.amber[900],
            child: const Center(child: Text('Entry A')),
          ),
          Container(
            width: MediaQuery.of(context).size.width/3,
            color: Colors.amber[800],
            child: const Center(child: Text('Entry B')),
          ),
          Container(
            width: MediaQuery.of(context).size.width/3,
            color: Colors.amber[700],
            child: const Center(child: Text('Entry C')),
          ),
          Container(
            width: MediaQuery.of(context).size.width/3,
            color: Colors.amber[600],
            child: const Center(child: Text('Entry D')),
          ),
          Container(
            width: MediaQuery.of(context).size.width/3,
            color: Colors.amber[500],
            child: const Center(child: Text('Entry E')),
          ),
          Container(
            width: MediaQuery.of(context).size.width/3,
            color: Colors.amber[400],
            child: const Center(child: Text('Entry F')),
          ),
          Container(
            width: MediaQuery.of(context).size.width/3,
            color: Colors.amber[300],
            child: const Center(child: Text('Entry G')),
          ),
        ],
      ),
    );
  }
}

class SnapScrollSize extends ScrollPhysics {
  const SnapScrollSize({super.parent, required this.snapSize});

  final double snapSize;

  @override
  SnapScrollSize applyTo(ScrollPhysics? ancestor) {
    return SnapScrollSize(parent: buildParent(ancestor), snapSize: snapSize);
  }

  double _getPage(ScrollMetrics position) {
    return position.pixels / snapSize;
  }

  double _getPixels(ScrollMetrics position, double page) {
    return page * snapSize;
  }

  double _getTargetPixels(
      ScrollMetrics position, Tolerance tolerance, double velocity) {
    double page = _getPage(position);
    if (velocity < -tolerance.velocity) {
      page -= 0.5;
    } else if (velocity > tolerance.velocity) {
      page += 0.5;
    }
    return _getPixels(position, page.roundToDouble());
  }

  @override
  Simulation? createBallisticSimulation(
      ScrollMetrics position, double velocity) {
    // If we're out of range and not headed back in range, defer to the parent
    // ballistics, which should put us back in range at a page boundary.
    if ((velocity <= 0.0 && position.pixels <= position.minScrollExtent) ||
        (velocity >= 0.0 && position.pixels >= position.maxScrollExtent)) {
      return super.createBallisticSimulation(position, velocity);
    }
    final Tolerance tolerance = this.tolerance;
    final double target = _getTargetPixels(position, tolerance, velocity);
    if (target != position.pixels) {
      return ScrollSpringSimulation(spring, position.pixels, target, velocity,
          tolerance: tolerance);
    }
    return null;
  }

  @override
  bool get allowImplicitScrolling => false;
}

相关问题