【-Flutter 组件-】ListWheelViewport 组件介绍
主题列表:juejin, github, smartblue, cyanosis, channing-cyan, fancy, hydrogen, condensed-night-purple, greenwillow, v-green, vue-pro, healer-readable, mk-cute, jzman, geek-black, awesome-green贡献主题:http...
主题列表:juejin, github, smartblue, cyanosis, channing-cyan, fancy, hydrogen, condensed-night-purple, greenwillow, v-green, vue-pro, healer-readable, mk-cute, jzman, geek-black, awesome-green
贡献主题:https://github.com/xitu/juejin-markdown-themes
theme: condensed-night-purple
highlight:
秉承着
有对象,用对象;没对象,找对象;找不到,造对象
的思想方针,终于将ListWheelViewport
组件跑起来了。以前由于认知的局限,一直没能玩转ListWheelViewport
,如今,确实成长了一些。此组件已收录于 FlutterUnit ,目前收录组件已破 310+,可喜可贺,欢迎 star 。
先看一下 ListWheelViewport
的基本信息:
dart 源码位置: flutter/lib/src/widgets/list_wheel_scroll_view.dart 父类: RenderObjectWidget 相关组件: ListWheelScrollView、CupertinoPicker、CupertinoDatePicker
ListWheelViewport
可以实现如下的滚动视口效果,可能你用过Cupertino
风格的选择器,觉得很类似。不错,它们的底层都有ListWheelViewport
的参与。了解ListWheelViewport
后,其他的都是弟弟。
一、 ListWheelViewport
三个必须属性
| 属性名 | 类型 | 默认值 | 介绍 |
| ------------- | ---------------------- | -------- | ------------------ | | itemExtent | double | required | 主轴方向 item 尺寸 |
| offset | ViewportOffset | required | 视口偏移 |
| childDelegate | ListWheelChildDelegate | required | 孩子代理构造器 |
下面先用一个最简的 demo 来测试一下
ListWheelViewport
的使用。上面的三个属性是必须给出的。
其中itemExtent
是最简单的,代表主轴方向 item 尺寸
。如下轮子上下滑滚动
,主轴就是 Y 轴,itemExtent 就表示每个 item 的高度
。
childDelegate
属性是ListWheelChildDelegate
类型的,其为抽象类,实现类有如下三个,
其中:ListWheelChildListDelegate
接受List<Widget>
进行展示。ListWheelChildBuilderDelegate
通过builder
构造器创建 item。ListWheelChildLoopingListDelegate
是可以无限滑动的列表,接受List<Widget>
进行展示。
offset
属性需要传入ViewportOffset
对象,这个对象造是很难造出来。不过凭借着之前的经验知道,这个对象可以通过Scrollable
中获得。在viewportBuilder
属性赋值时,可以回调ViewportOffset
对象。
```dart typedef ViewportBuilder = Widget Function(BuildContext context, ViewportOffset position);
class Scrollable extends StatefulWidget { const Scrollable({ Key? key, this.axisDirection = AxisDirection.down, this.controller, this.physics, required this.viewportBuilder, //<---- viewportBuilder this.incrementCalculator, this.excludeFromSemantics = false, this.semanticChildCount, this.dragStartBehavior = DragStartBehavior.start, this.restorationId, ```
使用将这几个要素合在一起,就可以将
ListWheelViewport
用起来。代码如下:
```dart class ListWheelViewportDemo extends StatelessWidget { final List data = [ Colors.blue[50], Colors.blue[100], Colors.blue[200], Colors.blue[300], Colors.blue[400], Colors.blue[500], Colors.blue[600], Colors.blue[700], Colors.blue[800], Colors.blue[900], Colors.blue[800], Colors.blue[700], Colors.blue[600], Colors.blue[500], Colors.blue[400], Colors.blue[300], Colors.blue[200], Colors.blue[100], ];
@override Widget build(BuildContext context) { return Container( height: 250, width: 320, child: Scrollable( axisDirection: AxisDirection.down, physics: BouncingScrollPhysics(), dragStartBehavior: DragStartBehavior.start, viewportBuilder: (ctx, position) => ListWheelViewport( itemExtent: 50, offset: position, childDelegate: ListWheelChildLoopingListDelegate( children: data.map((e) => _buildItem(e)).toList()), )), ); }
Widget _buildItem(Color color) => Container( alignment: Alignment.center, color: color, child: Text(colorString(color), style: TextStyle(color: Colors.white, shadows: [ Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2) ])), );
String colorString(Color color) => "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; } ```
看下 itemExtent 属性的作用
| itemExtent = 80 | itemExtent = 100 | | ---- | ---- | | | |
二、 perspective、squeeze、diameterRatio 效果属性
| 属性名 | 类型 | 默认值 | 介绍 | | ------------- | ------ | ------ | --------------- | | perspective | double | 0.003 | 透视参数 0~0.01 | | squeeze | double | 1.0 | 挤压值 | | diameterRatio | double | 2.0 | 直径分率 |
1. perspective 属性
perspective
是透视
的意思,默认是0.003
,取值范围在0~0.01
之间。
dart ---->[RenderListWheelViewport]---- static const double defaultPerspective = 0.003;
- 这是
perspective:0.01
的效果
- 这是
perspective:0.001
的效果,可见perspective
数值越大,透视效果越强。
2. squeeze 属性
squeeze
是挤压
的意思,默认是1.0
。
- 这是
squeeze:0.8
的效果
- 这是
squeeze:1.5
的效果,可见squeeze
可以控制 item 的松散程度
3. diameterRatio 属性
diameterRatio : 圆柱直径与主轴视口大小比率。
| diameterRatio = 2 | diameterRatio = pi/2 | | ---- | ---- | | | |
三、 其他属性
| 属性名 | 类型 | 默认值 | 介绍 | | ------------- | ------ | ------ | -------- | | magnification | double | 1.0 | 放大比例 | | useMagnifier | bool | false | 是否放大 | | clipBehavior | Clip | Clip.hardEdge | 剪裁行为 | | renderChildrenOutsideViewport | bool | false | 出视野是否渲染 | | offAxisFraction | double | 0.0 | 轴中心偏移比 | | overAndUnderCenterOpacity | double | 1 | 放大器之外的透明度 |
1. 放大效果
该组件自带如下
放大效果
,通过magnification
和useMagnifier
控制。
dart @override Widget build(BuildContext context) { return Container( height: 250, width: 320, child: Scrollable( axisDirection: AxisDirection.down, physics: BouncingScrollPhysics(), dragStartBehavior: DragStartBehavior.start, viewportBuilder: (ctx, position) => ListWheelViewport( perspective: 0.008, squeeze: 1, diameterRatio: 2, itemExtent: 50, useMagnifier: true, magnification: 2, offset: position, childDelegate: ListWheelChildLoopingListDelegate( children: data.map((e) => _buildItem(e)).toList()), )), ); }
2. 出界渲染与裁剪
默认情况下,item 不在视野区域内不会渲染,可以通过
renderChildrenOutsideViewport:true
让其显示,注意此时 clipBehavior 必须为 Clip.none
。效果如下:
dart @override Widget build(BuildContext context) { return Container( height: 250, width: 320, child: Scrollable( axisDirection: AxisDirection.down, physics: BouncingScrollPhysics(), dragStartBehavior: DragStartBehavior.start, viewportBuilder: (ctx, position) => ListWheelViewport( perspective: 0.008, squeeze: 1, diameterRatio: 2, renderChildrenOutsideViewport: true, clipBehavior: Clip.none, itemExtent: 50, offset: position, childDelegate: ListWheelChildLoopingListDelegate( children: data.map((e) => _buildItem(e)).toList()), )), ); }
3. offAxisFraction
和 overAndUnderCenterOpacity
属性
offAxisFraction: 0.2
效果
overAndUnderCenterOpacity:0.4
效果
四、 基于 ListWheelViewport
实现的组件们
1. ListWheelScrollView
组件
底层基于
_FixedExtentScrollable(Scrollable子类)
和ListWheelViewport
实现,此组件除了视口之外,还额外拥有监听滑动 item
的能力。如下,在上面的小圆颜色有下面滚轮滑动时选中色决定。ListWheelViewport
的相关属性,在ListWheelScrollView
中效果是一致的。
```dart class CustomListWheelScrollView extends StatefulWidget { @override _CustomListWheelScrollViewState createState() => _CustomListWheelScrollViewState(); }
class _CustomListWheelScrollViewState extends State { var data = [ Colors.orange[50], Colors.orange[100], Colors.orange[200], Colors.orange[300], Colors.orange[400], Colors.orange[500], Colors.orange[600], Colors.orange[700], Colors.orange[800], Colors.orange[900] ];
Color _color = Colors.blue;
@override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ _buildCircle(), Container( height: 150, width: 300, child: ListWheelScrollView.useDelegate( childDelegate: ListWheelChildLoopingListDelegate( children: data.map((e) => _buildItem(e)).toList()), perspective: 0.006, itemExtent: 50, onSelectedItemChanged: (index) { setState(() => _color = data[index]); }, ), ), ], ); }
Widget _buildCircle() => Container( margin: EdgeInsets.only(bottom: 5), width: 30, height: 30, decoration: BoxDecoration(color: _color, shape: BoxShape.circle), );
Widget _buildItem(Color color) { return Container( key: ValueKey(color), alignment: Alignment.center, height: 50, color: color, child: Text( colorString(color), style: TextStyle(color: Colors.white, shadows: [ Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2) ]), ), ); }
String colorString(Color color) => "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; } ```
2. CupertinoPicker
组件
CupertinoPicker
的内部源码实现依赖于ListWheelScrollView
。所以最终的效果还是ListWheelViewport
的功劳。
```dart class CustomCupertinoPicker extends StatelessWidget { final names = [ 'Java', 'Kotlin', 'Dart', 'Swift', 'C++', 'Python', "JavaScript", "PHP", "Go", "Object-c" ];
@override Widget build(BuildContext context) { return Container( height: 150, width: 300, child: CupertinoPicker( backgroundColor: CupertinoColors.systemGrey.withAlpha(33), diameterRatio: 1, offAxisFraction: 0.2, squeeze: 1.5, itemExtent: 40, onSelectedItemChanged: (position) { print('当前条目 ${names[position]}'); }, children: names.map((e) => Center(child: Text(e))).toList()), ); } } ```
3. CupertinoDatePicker
组件
CupertinoDatePicker
内部是基于CupertinoPicker
实现的。
```dart class CustomCupertinoDatePicker extends StatefulWidget { @override _CustomCupertinoDatePickerState createState() => _CustomCupertinoDatePickerState(); }
class _CustomCupertinoDatePickerState extends State { DateTime _date = DateTime.now();
@override Widget build(BuildContext context) { return Container( width: 350, child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( '当前日期:${_date.toIso8601String()}', style: TextStyle(color: Colors.grey, fontSize: 16), ), _buildInfoTitle('CupertinoDatePickerMode.dateAndTime'), buildPicker(CupertinoDatePickerMode.dateAndTime), ], ), ); }
Container buildPicker(CupertinoDatePickerMode mode) { return Container( margin: EdgeInsets.all(10), height: 150, child: CupertinoDatePicker( mode: mode, initialDateTime: DateTime.now(), minimumYear: 2018, maximumYear: 2030, use24hFormat: false, minuteInterval: 1, backgroundColor: CupertinoColors.white, onDateTimeChanged: (date) { print(date); setState(() => _date = date); }, ), ); }
Widget _buildInfoTitle(info) { return Padding( padding: const EdgeInsets.only(left: 20, top: 20, bottom: 5), child: Text( info, style: TextStyle( color: Colors.blue, fontSize: 16, fontWeight: FontWeight.bold), ), ); } } ```
4. 小结一下
这样来看
滚轮
相关的组件,追其本源都与ListWheelViewport
相关。所以认识了ListWheelViewport
各属性的意义,则其他衍生出来的组件就更容易理解了。这便是以不变,应万变
。也许某一天,你会遇到自定义某种滚轮效果
,这时候ListWheelViewport
定可住你一臂之力。
shell 【1】ListWheelScrollView 是基于 Scrollable + ListWheelViewport 实现的。 【2】CupertinoPicker 是基于 ListWheelScrollView 实现的。 【3】CupertinoDatePicker 是基于 CupertinoPicker 实现的。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)