老孟導讀:Flutter 1.20 更新了 Slider、RangeSlider、日期選擇器組件、時間選擇器組件的樣式,新增了交換組件:InteractiveViewer,下面詳細介紹其用法。ios
Flutter 1.20 版本將 Slider 和 RangeSlider 小部件更新爲最新的 Material 準則。新的滑塊在設計時考慮到了更好的可訪問性:軌道更高,滑塊帶有陰影,而且值指示器具備新的形狀和改進的文本縮放支持。git
基礎用法:微信
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; }); }, ) ], ), ), ); } }
看看 Flutter 1.20 版本之前的樣式(個人珍藏):app
明顯的感受就是滑塊軌道變粗了,滑塊變的更有立體感(加了陰影)了。less
Slider 默認滑動範圍是 0-1,修改成 1-100:async
Slider( value: _sliderValue, min: 1, max: 100, onChanged: (v){ setState(() { _sliderValue = v; }); }, )
設置滑塊的滑動爲 離散的,即滑動值爲 0、25 、50、75 100:ide
Slider( value: _sliderValue, min: 0, max: 100, divisions: 4, onChanged: (v){ setState(() { _sliderValue = v; }); }, )
設置標籤,滑動過程當中在其上方顯示:ui
Slider( value: _sliderValue, label: '$_sliderValue', min: 0, max: 100, divisions: 4, onChanged: (v){ setState(() { _sliderValue = v; }); }, )
看看 Flutter 1.20 版本之前的樣式(依然是個人珍藏):設計
我的感受之前的更好看。3d
下面是官方給的 Slider 結構圖:
自定義滑塊 激活的顏色 和 未激活的顏色:
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 和 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,使用 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; }); }, )
Flutter 1.20 版本更新了 日期 類組件的樣式,加入了新的緊湊設計以及對日期範圍的支持。
輸入模式 結構圖:
點擊按鈕彈出日期組件:
RaisedButton( child: Text('彈出日期組件'), onPressed: () async { 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), 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( 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') );
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: '肯定');
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, ); }, );
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'); }, )
4 | 14 | PM
July | 13 | 2012
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'); }, ), )
CupertinoTimerPicker 是 iOS風格的時間選擇器。
CupertinoTimerPicker(onTimerDurationChanged: (time) { print('$time'); })
設置顯示模式:
16 hours | 14 min
14 min | 43 sec
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 是 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, ), ], ), ], ), ), ), ), ); } }
回調事件:
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】: