【Flutter 实战】1.20版本更新及新增组件
source link: https://segmentfault.com/a/1190000024419315
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
老孟导读 :Flutter 1.20 更新了 Slider、RangeSlider、日期选择器组件、时间选择器组件的样式,新增了交换组件:InteractiveViewer,下面详细介绍其用法。
滑块
Flutter 1.20 版本将 Slider 和 RangeSlider 小部件更新为最新的 Material 准则。新的滑块在设计时考虑到了更好的可访问性:轨道更高,滑块带有阴影,并且值指示器具有新的形状和改进的文本缩放支持。
Slider
基础用法:
class SliderDemo extends StatefulWidget { @override _SliderDemoState createState() => _SliderDemoState(); } class _SliderDemoState extends State<SliderDemo> { double _sliderValue = 0; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Text('值:$_sliderValue'), Slider( value: _sliderValue, onChanged: (v){ setState(() { _sliderValue = v; }); }, ) ], ), ), ); } }
- value :当前值。
- onChanged :滑块值改变时回调。
看看 Flutter 1.20 版本以前的样式(我的珍藏):
明显的感觉就是滑块轨道变粗了,滑块变的更有立体感(加了阴影)了。
Slider默认滑动范围是 0-1,修改为 1-100:
Slider( value: _sliderValue, min: 1, max: 100, onChanged: (v){ setState(() { _sliderValue = v; }); }, )
设置滑块的滑动为 离散的 ,即滑动值为 0、25 、50、75 100:
Slider( value: _sliderValue, min: 0, max: 100, divisions: 4, onChanged: (v){ setState(() { _sliderValue = v; }); }, )
设置标签,滑动过程中在其上方显示:
Slider( value: _sliderValue, label: '$_sliderValue', min: 0, max: 100, divisions: 4, onChanged: (v){ setState(() { _sliderValue = v; }); }, )
看看 Flutter 1.20 版本以前的样式(依然是我的珍藏):
个人感觉以前的更好看。
下面是官方给的 Slider 结构图:
- 1 :轨道(Track),1 和 4 是有区别的,1 指的是底部整个轨道,轨道显示了可供用户选择的范围。对于从左到右(LTR)的语言,最小值出现在轨道的最左端,而最大值出现在最右端。对于从右到左(RTL)的语言,此方向是相反的。
- 2 :滑块(Thumb),位置指示器,可以沿着轨道移动,显示其位置的选定值。
- 3 :标签(label),显示与滑块的位置相对应的特定数字值。
- 4 :刻度指示器(Tick mark),表示用户可以将滑块移动到的预定值。
自定义滑块 激活的颜色 和 未激活的颜色 :
Slider( activeColor: Colors.red, inactiveColor: Colors.blue, value: _sliderValue, label: '$_sliderValue', min: 0, max: 100, divisions: 4, onChanged: (v){ setState(() { _sliderValue = v; }); }, )
这个自定义比较笼统,下面来一个更细致的自定义:
SliderTheme( data: SliderTheme.of(context).copyWith( activeTrackColor: Color(0xff404080), thumbColor: Colors.blue, overlayColor: Colors.green, valueIndicatorColor: Colors.purpleAccent), child: Slider( value: _sliderValue, label: '$_sliderValue', min: 0, max: 100, divisions: 4, onChanged: (v) { setState(() { _sliderValue = v; }); }, ), )
这个基本可以完全自定义样式了。
如何在 Flutter 1.20 版本使用以前的标签样式呢?
SliderTheme( data: SliderTheme.of(context).copyWith( valueIndicatorShape: PaddleSliderValueIndicatorShape(), ), child: Slider( value: _sliderValue, label: '$_sliderValue', min: 0, max: 100, divisions: 4, onChanged: (v) { setState(() { _sliderValue = v; }); }, ), )
RectangularSliderValueIndicatorShape表示矩形样式:
RangeSlider
RangeSlider和 Slider 几乎一样, RangeSlider 是范围滑块,想要选择一段值,可以使用 RangeSlider。
RangeValues _rangeValues = RangeValues(0, 25); RangeSlider( values: _rangeValues, labels: RangeLabels('${_rangeValues.start}','${_rangeValues.end}'), min: 0, max: 100, divisions: 4, onChanged: (v) { setState(() { _rangeValues = v; }); }, ),
滑块状态
ios风格的 Slider
ios风格的 Slider,使用 CupertinoSlider:
double _sliderValue = 0; CupertinoSlider( value: _sliderValue, onChanged: (v) { setState(() { _sliderValue = v; }); }, )
当然也可以根据平台显示不同风格的Slider,ios平台显示CupertinoSlider效果,其他平台显示Material风格,用法如下:
Slider.adaptive( value: _sliderValue, onChanged: (v) { setState(() { _sliderValue = v; }); }, )
Material风格日期选择器
Flutter 1.20 版本更新了 日期 类组件的样式,加入了新的紧凑设计以及对日期范围的支持。
showDatePicker
结构图
- 标题
- 选中的日期
- 切换到 输入模式
- 年 选择菜单
- 月份分页
- 当前时间
- 选中日期
输入模式结构图:
- 标题
- 选中日期
- 切换 日历模式
- 输入框
基础用法
点击按钮弹出日期组件:
RaisedButton( child: Text('弹出日期组件'), onPressed: () async { await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(2010), lastDate: DateTime(2025), );
- initialDate :初始化时间,通常情况下设置为当前时间。
- firstDate :表示开始时间,不能选择此时间前面的时间。
- lastDate :表示结束时间,不能选择此时间之后的时间。
设置日期选择器对话框的模式:
var result = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(2010), lastDate: DateTime(2025), initialEntryMode: DatePickerEntryMode.input, );
直接显示 输入模式 ,默认是 日历模式 。
设置日历日期选择器的初始显示,包含 day 和 year :
var result = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(2010), lastDate: DateTime(2025), initialDatePickerMode: DatePickerMode.year, );
和以前的版本对比:
设置顶部标题、取消按钮、确定按钮 文案:
var result = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(2010), lastDate: DateTime(2025), helpText: '选则日期', cancelText: '取消', confirmText: '确定', );
修改 输入模式 下文案:
var result = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(2010), lastDate: DateTime(2025), errorFormatText: '错误的日期格式', errorInvalidText: '日期格式非法', fieldHintText: '月/日/年', fieldLabelText: '填写日期', );
设置可选日期范围
var result = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(2010), lastDate: DateTime(2025), selectableDayPredicate: (date) { return date.difference(DateTime.now()).inMilliseconds < 0; }, );
今天以后的日期全部为灰色,不可选状态。
设置深色主题
设置深色主题使 builder
,其用于包装对话框窗口小部件以添加继承的窗口小部件,例如 Theme
,设置深色主题如下:
var result = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(2010), lastDate: DateTime(2025), builder: (context,child){ return Theme( data: ThemeData.dark(), child: child, ); } );
获取选中的日期
showDatePicker 方法是 Future 方法,点击日期选择控件的确定按钮后,返回选择的日期。
var result = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(2010), lastDate: DateTime(2025), ); print('$result');
result 为选择的日期。
CalendarDatePicker
日期组件直接显示在页面上,而不是弹出显示:
CalendarDatePicker( initialDate: DateTime.now(), firstDate: DateTime(2010), lastDate: DateTime(2025), onDateChanged: (d) { print('$d'); }, )
其参数和 showDatePicker 一样。
范围日期
选择范围日期使用 showDateRangePicker :
RaisedButton( child: Text('范围日期'), onPressed: () async { var date = showDateRangePicker(context: context, firstDate: DateTime(2010), lastDate: DateTime(2025)); }, ),
其参数和 showDatePicker 一样。
范围日期结构图:
- 标题
- 选定的日期范围
- 切换到 输入模式
- 月和年标签
- 当前时间
- 开始时间
- 选中时间范围
- 结束时间
国际化
国际化都是一个套路,下面以 showDatePicker 为例:
在 pubspec.yaml 中引入:
dependencies: flutter_localizations: sdk: flutter
在顶级组件 MaterialApp 添加支持:
MaterialApp( title: 'Flutter Demo', localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], supportedLocales: [ const Locale('zh'), const Locale('en'), ], ...
弹出日期组件:
var result = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(2010), lastDate: DateTime(2025), );
此时将系统语音调整为 中文 :
此组件只支持 中文 ,不管系统设置语言:
var result = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(2010), lastDate: DateTime(2025), locale: Locale('zh') );
Material风格时间选择器
Flutter 1.20 版本更新了 时间 类组件的样式。
基础使用
弹出时间组件:
RaisedButton( child: Text('弹出时间选择器'), onPressed: () async { var result = showTimePicker(context: context, initialTime: TimeOfDay.now()); }, )
1.20 版以前的效果:
设置 交互模式 ,交互模式包含 时钟模式 (默认)和 输入模式 。
var result = showTimePicker( context: context, initialTime: TimeOfDay.now(), initialEntryMode: TimePickerEntryMode.input);
时钟模式(TimePickerEntryMode.dial):
输入模式(TimePickerEntryMode.input):
设置顶部标题、取消按钮、确定按钮 文案:
var result = showTimePicker( context: context, initialTime: TimeOfDay.now(), initialEntryMode: TimePickerEntryMode.input, helpText: '选择时间', cancelText: '取消', confirmText: '确定');
24小时 制:
var result = showTimePicker( context: context, initialTime: TimeOfDay.now(), builder: (BuildContext context, Widget child) { return MediaQuery( data: MediaQuery.of(context) .copyWith(alwaysUse24HourFormat: true), child: child, ); }, );
黑暗模式
var result = showTimePicker( context: context, initialTime: TimeOfDay.now(), builder: (BuildContext context, Widget child) { return Theme( data: ThemeData.dark(), child: child, ); }, );
国际化
在 pubspec.yaml 中引入:
dependencies: flutter_localizations: sdk: flutter
在顶级组件 MaterialApp 添加支持:
MaterialApp( title: 'Flutter Demo', localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], supportedLocales: [ const Locale('zh'), const Locale('en'), ], ...
弹出时间组件:
RaisedButton( child: Text('弹出时间选择器'), onPressed: () async { var result = showTimePicker(context: context, initialTime: TimeOfDay.now()); }, )
切换系统语言为中文:
不跟随系统语言,直接指定,比如当前系统语言为中文,指定为英文:
var result = showTimePicker( context: context, initialTime: TimeOfDay.now(), builder: (BuildContext context, Widget child) { return Localizations( locale: Locale('en'), delegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, ], child: child, ); }, );
iOS风格日期选择器
基础使用
CupertinoDatePicker是 iOS风格的日期选择器。
class CupertinoDatePickerDemo extends StatefulWidget { @override _CupertinoDatePickerDemoState createState() => _CupertinoDatePickerDemoState(); } class _CupertinoDatePickerDemoState extends State<CupertinoDatePickerDemo> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: Center( child: Container( height: 200, color: Colors.grey.withOpacity(.5), child: CupertinoDatePicker( initialDateTime: DateTime.now(), onDateTimeChanged: (date) { print('$date'); }, ), ), ), ); } }
设置最大/小时间:
CupertinoDatePicker( initialDateTime: DateTime.now(), minimumDate: DateTime.now().add(Duration(days: -1)), maximumDate: DateTime.now().add(Duration(days: 1)), onDateTimeChanged: (date) { print('$date'); }, )
最大时间为明天,最小时间为昨天:
设置模式为 时间 :
CupertinoDatePicker( mode: CupertinoDatePickerMode.time, initialDateTime: DateTime.now(), onDateTimeChanged: (date) { print('$date'); }, )
设置模式为 日期 :
CupertinoDatePicker( mode: CupertinoDatePickerMode.date, initialDateTime: DateTime.now(), onDateTimeChanged: (date) { print('$date'); }, )
设置模式为 日期和时间 :
CupertinoDatePicker( mode: CupertinoDatePickerMode.dateAndTime, initialDateTime: DateTime.now(), onDateTimeChanged: (date) { print('$date'); }, )
-
time
:只显示时间,效果:
4 | 14 | PM
-
date
:只显示日期,效果:
July | 13 | 2012
-
dateAndTime
:时间和日期都显示,效果:
Fri Jul 13 | 4 | 14 | PM
使用24小时制:
CupertinoDatePicker( use24hFormat: true, initialDateTime: DateTime.now(), onDateTimeChanged: (date) { print('$date'); }, )
国际化
在 pubspec.yaml 中引入:
dependencies: flutter_localizations: sdk: flutter
在顶级组件 MaterialApp 添加支持:
MaterialApp( title: 'Flutter Demo', localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], supportedLocales: [ const Locale('zh'), const Locale('en'), ], ...
组件使用:
CupertinoDatePicker( initialDateTime: DateTime.now(), onDateTimeChanged: (date) { print('$date'); }, )
组件语言跟随系统语言,当前系统语言为英文,效果:
不跟随系统语言,直接指定,比如当前系统语言为英文,指定为中文:
Localizations( locale: Locale('zh'), delegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], child: CupertinoDatePicker( initialDateTime: DateTime.now(), onDateTimeChanged: (date) { print('$date'); }, ), )
iOS风格时间选择器
基础使用
CupertinoTimerPicker是 iOS风格的时间选择器。
CupertinoTimerPicker(onTimerDurationChanged: (time) { print('$time'); })
设置显示模式:
-
CupertinoTimerPickerMode.hm
:显示 小时 | 分钟,英文效果
16 hours | 14 min
-
CupertinoTimerPickerMode.ms
: 显示 分钟 | 秒,英文效果
14 min | 43 sec
-
CupertinoTimerPickerMode.hms
:显示 小时 | 分钟 | 秒,英文效果
16 hours | 14 min | 43 sec
CupertinoTimerPicker( mode: CupertinoTimerPickerMode.hm, onTimerDurationChanged: (time) { print('$time'); })
默认情况下,CupertinoTimerPicker显示0:0:0,设置显示当前时间:
CupertinoTimerPicker( initialTimerDuration: Duration( hours: DateTime.now().hour, minutes: DateTime.now().minute, seconds: DateTime.now().second), onTimerDurationChanged: (time) { print('$time'); })
设置 分/秒 的间隔:
CupertinoTimerPicker( minuteInterval: 5, secondInterval: 5, onTimerDurationChanged: (time) { print('$time'); })
国际化
在 pubspec.yaml 中引入:
dependencies: flutter_localizations: sdk: flutter
在顶级组件 MaterialApp 添加支持:
MaterialApp( title: 'Flutter Demo', localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], supportedLocales: [ const Locale('zh'), const Locale('en'), ], ...
组件使用:
CupertinoTimerPicker(onTimerDurationChanged: (time) { print('$time'); })
组件语言跟随系统语言,当前系统语言为英文,效果:
不跟随系统语言,直接指定,比如当前系统语言为英文,指定为中文:
Localizations( locale: Locale('zh'), delegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], child: CupertinoTimerPicker(onTimerDurationChanged: (time) { print('$time'); }), )
InteractiveViewer
InteractiveViewer是 Flutter 1.20 新增的组件,用户可以通过拖动以平移、缩放和拖放子组件。
InteractiveViewer( child: Image.asset('assets/images/go_board_09x09.png'), )
alignPanAxis参数表示是否只在水平和垂直方向上拖拽,默认为false,设置为true,无法沿着对角线(斜着)方向移动。
InteractiveViewer( alignPanAxis: true, child: Image.asset('assets/images/go_board_09x09.png'), )
maxScale、 minScale 、 scaleEnabled 是缩放相关参数,分别表示最大缩放倍数、最小缩放倍数、是否可以缩放:
InteractiveViewer( maxScale: 2, minScale: 1, scaleEnabled: true, child: Image.asset('assets/images/go_board_09x09.png'), )
constrained参数表示组件树中的约束是否应用于子组件,默认为true,如果设为true,表示子组件是无限制约束,这对子组件的尺寸比 InteractiveViewer 大时非常有用,比如子组件为滚动系列组件。
如下的案例,子组件为 Table,Table 尺寸大于屏幕,必须将 constrained
设置为 false 以便将其绘制为完整尺寸。超出的屏幕尺寸可以平移到视图中。
class InteractiveViewerDemo extends StatelessWidget { @override Widget build(BuildContext context) { const int _rowCount = 20; const int _columnCount = 10; return Scaffold( appBar: AppBar(), body: Center( child: Container( height: 300, width: 300, child: InteractiveViewer( constrained: false, child: Table( columnWidths: <int, TableColumnWidth>{ for (int column = 0; column < _columnCount; column += 1) column: const FixedColumnWidth(100.0), }, children: <TableRow>[ for (int row = 0; row < _rowCount; row += 1) TableRow( children: <Widget>[ for (int column = 0; column < _columnCount; column += 1) Container( height: 50, color: row % 2 + column % 2 == 1 ? Colors.red : Colors.green, ), ], ), ], ), ), ), ), ); } }
回调事件:
- onInteractionStart :当用户开始平移或缩放手势时调用。
- onInteractionUpdate :当用户更新组件上的平移或缩放手势时调用。
- onInteractionEnd :当用户在组件上结束平移或缩放手势时调用。
InteractiveViewer( child: Image.asset('assets/images/go_board_09x09.png'), onInteractionStart: (ScaleStartDetails scaleStartDetails){ print('onInteractionStart:$scaleStartDetails'); }, onInteractionUpdate: (ScaleUpdateDetails scaleUpdateDetails){ print('onInteractionUpdate:$scaleUpdateDetails'); }, onInteractionEnd: (ScaleEndDetails endDetails){ print('onInteractionEnd:$endDetails'); }, )
通过 Matrix4 矩阵对其进行变换,比如左移、放大等,添加变换控制器:
final TransformationController _transformationController = TransformationController(); InteractiveViewer( child: Image.asset('assets/images/go_board_09x09.png'), transformationController: _transformationController, )
放大变换:
var matrix = _transformationController.value.clone(); matrix.scale(1.5, 1.0, 1.0); _transformationController.value = matrix;
完整代码:
import 'dart:math'; import 'package:flutter/material.dart'; /// /// desc: /// class InteractiveViewerDemo extends StatefulWidget { @override _InteractiveViewerDemoState createState() => _InteractiveViewerDemoState(); } class _InteractiveViewerDemoState extends State<InteractiveViewerDemo> { final TransformationController _transformationController = TransformationController(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: Column( children: [ Container( padding: EdgeInsets.symmetric(horizontal: 10.0), child: Center( child: InteractiveViewer( child: Image.asset('assets/images/go_board_09x09.png'), transformationController: _transformationController, ), ), ), Expanded( child: Container(), ), Row( children: [ RaisedButton( child: Text('重置'), onPressed: () { _transformationController.value = Matrix4.identity(); }, ), RaisedButton( child: Text('左移'), onPressed: () { var matrix = _transformationController.value.clone(); matrix.translate(-5.0); _transformationController.value = matrix; }, ), RaisedButton( child: Text('放大'), onPressed: () { var matrix = _transformationController.value.clone(); matrix.scale(1.5, 1.0, 1.0); _transformationController.value = matrix; }, ), ], ), ], ), ); } }
交流
老孟Flutter博客地址(330个控件用法): http://laomengit.com
欢迎加入Flutter交流群(微信:laomengit)、关注公众号【老孟Flutter】:
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK