延续上一节的内容,上一节我们讲解了各种Dialog跟提示,其中包括轻量级跟非轻量级,我们了解到了像SnackBar、Tooltip、Dialog等各种具有提示交互作用的Widget,今天我们继续上一篇的内容,来一块了解一下各种用于进度显示的Widget跟checkBox等选择作用的Widget。

今天我们来一块了解一下各种Index跟Chose控件,像 CircularProgressIndicator圆环进度条,
LinearProgressIndicator水平进度条, Slider滑杆,还有Checkbox,Switch等,下面我们就一块来进入今天的分享。

1. CircularProgressIndicator

老规矩,先从构造方法入手

  1. const CircularProgressIndicator({
  2. Key key,
  3. double value,
  4. Color backgroundColor,
  5. Animation<Color> valueColor,
  6. this.strokeWidth = 4.0,
  7. String semanticsLabel,
  8. String semanticsValue,
  9. })

从上述 CircularProgressIndicator的方法签名中,我们首先可以得到的信息是,圆环进度条可以自定义指定背景颜色跟宽度,可以设置进度,我贴上几个简单的例子来直观体验一下。

  1. import 'package:flutter/material.dart';
  2. void main() {
  3. runApp(new MaterialApp(home: new FlutterDemo()));
  4. }
  5. class FlutterDemo extends StatelessWidget {
  6. @override
  7. Widget build(BuildContext context) {
  8. return new Scaffold(
  9. appBar: new AppBar(
  10. title: new Text("Flutter进阶之旅"),
  11. ),
  12. body: new Center(
  13. child: new Column(
  14. children: <Widget>[
  15. SizedBox(height: 30.0),
  16. Text("设置进度比为80%(0.8)"),
  17. SizedBox(height: 30.0),
  18. CircularProgressIndicator(
  19. value: 0.8,
  20. backgroundColor: Colors.green,
  21. strokeWidth: 10.0,
  22. ),
  23. SizedBox(height: 30.0),
  24. Text("未做任何处理,默认一直循环"),
  25. CircularProgressIndicator(),
  26. Text("设置圆环进度颜色为红色"),
  27. CircularProgressIndicator(
  28. valueColor: AlwaysStoppedAnimation(Colors.deepOrange),
  29. ),
  30. ],
  31. )),
  32. );
  33. }
  34. }

效果图
11- Flutter入门进阶之旅-Index-Chose - 图1

2.LinearProgressIndicator

对比 LinearProgressIndicator的构造方法

  1. const LinearProgressIndicator({
  2. Key key,
  3. double value,
  4. Color backgroundColor,
  5. Animation<Color> valueColor,
  6. String semanticsLabel,
  7. String semanticsValue,
  8. })

我们发现 LinearProgressIndicatorCircularProgressIndicator除了少了一个strokeWidth不支持设置进度条宽度之外,其他完全一致,所以我就不多做讲解了,直接看下效果吧。

效果图
11- Flutter入门进阶之旅-Index-Chose - 图2
样例代码

  1. import 'package:flutter/material.dart';
  2. void main() {
  3. runApp(new MaterialApp(home: new FlutterDemo()));
  4. }
  5. class FlutterDemo extends StatelessWidget {
  6. @override
  7. Widget build(BuildContext context) {
  8. return new Scaffold(
  9. appBar: new AppBar(
  10. title: new Text("Flutter进阶之旅"),
  11. ),
  12. body: new Center(
  13. child: new Column(
  14. children: <Widget>[
  15. SizedBox(height: 30.0),
  16. Text("设置进度比为80%(0.8)"),
  17. SizedBox(height: 30.0),
  18. LinearProgressIndicator(
  19. value: 0.8,
  20. backgroundColor: Colors.green,
  21. ),
  22. SizedBox(height: 30.0),
  23. Text("未做任何处理,默认一直循环"),
  24. LinearProgressIndicator(),
  25. Text("设置进度颜色为红色,背景透明"),
  26. LinearProgressIndicator(
  27. backgroundColor: Colors.transparent,
  28. valueColor: AlwaysStoppedAnimation(Colors.deepOrange),
  29. ),
  30. ],
  31. )),
  32. );
  33. }
  34. }

3.Slider

构造方法

  1. const Slider({
  2. Key key,
  3. @required this.value,
  4. @required this.onChanged,
  5. this.onChangeStart,
  6. this.onChangeEnd,
  7. this.min = 0.0,
  8. this.max = 1.0,
  9. this.divisions,
  10. this.label,
  11. this.activeColor,
  12. this.inactiveColor,
  13. this.semanticFormatterCallback,
  14. })

Slider滑杆进度条,支持用户手动拖拽定位,如果不涉及到用户拖拽交互,我们可以把他认为就是一个静态的 LinearProgressIndicator,使用起来也比较简单。但是如果需用跟用户交互,涉及到用户的拖拽手势然后动态的改变Slider的值,就需要借助我们之前讲到的 StatefullWidget,通过 setState来完成用户交互的数据重新更新渲染到Slider上,我们来一起体验一下。

3.1 静态Slider

  1. import 'package:flutter/material.dart';
  2. void main() {
  3. runApp(new MaterialApp(home: new FlutterDemo()));
  4. }
  5. class FlutterDemo extends StatelessWidget {
  6. @override
  7. Widget build(BuildContext context) {
  8. return new Scaffold(
  9. appBar: new AppBar(
  10. title: new Text("Flutter进阶之旅"),
  11. ),
  12. body: new Center(
  13. child: new Slider(
  14. value: 0.4,
  15. onChanged: null,
  16. ),
  17. ),
  18. );
  19. }
  20. }

效果图
11- Flutter入门进阶之旅-Index-Chose - 图3
如上图,就仅仅只是一个指示作用,不支持用户手指拖拽滑动,下面我们来借助StatefullWidget来做一下简单交互。

3.2 动态Slider

我们来模拟一下用户拖拽Slider可以设置当前是星期几的效果。
效果图
11- Flutter入门进阶之旅-Index-Chose - 图4
这里提到一个小细节,有读者给我反馈说 在使用Slider的时候,在Slider上方的label一直不显示,这里是因为label在指定了divisions也就是指定当前进度被离散成多少份之后才会显示,读者注意到这个细节即可。下面是上图的样例代码,供大家参考。

  1. import 'package:flutter/material.dart';
  2. void main() {
  3. runApp(new MaterialApp(home: new FlutterDemo()));
  4. }
  5. class FlutterDemo extends StatefulWidget {
  6. @override
  7. State<StatefulWidget> createState() => SliderState();
  8. }
  9. class SliderState extends State {
  10. double _currentIndex = 0.0;
  11. void _onSliderStateChanged(double value) {
  12. setState(() {
  13. _currentIndex = value;
  14. print(_currentIndex.toString() + '-------------------');
  15. });
  16. }
  17. @override
  18. Widget build(BuildContext context) {
  19. return new Scaffold(
  20. appBar: new AppBar(
  21. title: new Text("Flutter进阶之旅"),
  22. ),
  23. body: new Center(
  24. child: new Slider(
  25. value: _currentIndex,
  26. label: '星期${(_currentIndex*10).floor().toString()}',
  27. activeColor: Colors.redAccent,
  28. inactiveColor: Colors.grey,
  29. max: 0.7,
  30. min: 0.0,
  31. onChanged: _onSliderStateChanged,
  32. onChangeStart: (value){
  33. print('开始滑动-------------$value');
  34. },
  35. onChangeEnd: (value){
  36. print('结束滑动-------------$value');
  37. },
  38. divisions: 7,
  39. ),
  40. ),
  41. );
  42. }
  43. }

上面说完了常用的进度展示控件,我们通过上面的学习基本能处理业务上不同场景下如果选用进度Widget,下面我们来进入本篇的第二部分,关于Chose控件的介绍。

4.Checkbox

Checkbox跟Slider一样,因为需要处理或者说需要记录用户的选择状态,然后去更新 Checkbox的选中状态,所以理所当然我们也需要使用 StatefullWidget来完成这一操作。否则 Checkbox只能跟上面分析 Slider一样,只能一个静态展示,不能完成或者说记录用户的行为操作。

Checkbox使用跟原生Android一样,没有什么需要特别注意的知识点,我就不多做讲解了,下面我们一起来看一个例子。
11- Flutter入门进阶之旅-Index-Chose - 图5

样例代码

  1. import 'package:flutter/material.dart';
  2. void main() {
  3. runApp(new MaterialApp(home: new FlutterDemo()));
  4. }
  5. class FlutterDemo extends StatefulWidget {
  6. @override
  7. State<StatefulWidget> createState() => SliderState();
  8. }
  9. class SliderState extends State {
  10. bool isChecked = false;
  11. void _onCheckStateChanged(bool value) {
  12. setState(() {
  13. isChecked = value;
  14. });
  15. }
  16. @override
  17. Widget build(BuildContext context) {
  18. return new Scaffold(
  19. appBar: new AppBar(
  20. title: new Text("Flutter进阶之旅"),
  21. ),
  22. body: new Center(
  23. child: new Checkbox(
  24. value: isChecked,
  25. onChanged: _onCheckStateChanged,
  26. activeColor: Colors.green,
  27. ),
  28. ),
  29. );
  30. }
  31. }

5.Switch

SwitchCheckbox用户上类似,都是为了记录用户的选中状态,只不过是 Switch可定制的部分比 Checkbox更多一些,开发者可以加入更多的个性化定制,我们还是先从构造方法说起。

  1. const Switch({
  2. Key key,
  3. @required this.value,
  4. @required this.onChanged,
  5. this.activeColor,
  6. this.activeTrackColor,
  7. this.inactiveThumbColor,
  8. this.inactiveTrackColor,
  9. this.activeThumbImage,
  10. this.inactiveThumbImage,
  11. this.materialTapTargetSize,
  12. })

下面我把几个配置不同样式的Switch效果图贴上了,读者可根据代码自行分析其具体实现细节,我就不逐一讲解构造方法中各个属性的作用了。

效果图
11- Flutter入门进阶之旅-Index-Chose - 图6

这里说明一下,上图为什么点击其中一个 Switch另外两个也会跟着联动,原因是我在写测试代码的时候,为了减少代码的书写量,尽可能的让代码清晰,所以给三个 SwitchonChanged的时候绑定的是同一个函数回调,没有分开单独处理每个 Swtich的回调事件,读者知道即可,不用过于纠结。

如上图:
第一个Switch我指定了,激活跟非激活状态的圆点颜色跟滑动轨迹的颜色
第二个Switch我指定了,激活跟非激活状态下圆点是资源图片。
第三个Swtich没有做任何处理。

上述效果图代码如下:

  1. import 'package:flutter/material.dart';
  2. void main() {
  3. runApp(new MaterialApp(home: new FlutterDemo()));
  4. }
  5. class FlutterDemo extends StatefulWidget {
  6. @override
  7. State<StatefulWidget> createState() => SliderState();
  8. }
  9. class SliderState extends State {
  10. bool isChecked = false;
  11. void _onCheckStateChanged(bool value) {
  12. setState(() {
  13. isChecked = value;
  14. });
  15. }
  16. @override
  17. Widget build(BuildContext context) {
  18. return new Scaffold(
  19. appBar: new AppBar(
  20. title: new Text("Flutter进阶之旅"),
  21. ),
  22. body: new Center(
  23. child: Column(
  24. children: <Widget>[
  25. new Switch(
  26. value: isChecked,
  27. inactiveThumbColor: Colors.redAccent,
  28. inactiveTrackColor: Colors.brown,
  29. activeTrackColor: Colors.blue,
  30. onChanged: _onCheckStateChanged,
  31. activeColor: Colors.green,
  32. ),
  33. Text("指定激活跟非激活状态下,圆点都为图片"),
  34. new Switch(
  35. value: isChecked,
  36. inactiveThumbImage: AssetImage('images/a.png'),
  37. activeThumbImage: AssetImage('images/aaa.png'),
  38. onChanged: _onCheckStateChanged,
  39. activeColor: Colors.green,
  40. ),
  41. new Switch(
  42. value: isChecked,
  43. onChanged: _onCheckStateChanged,
  44. )
  45. ],
  46. ),
  47. ),
  48. );
  49. }
  50. }

属性及外观

Switch和Checkbox属性比较简单,读者可以查看API文档,它们都有一个activeColor属性,用于设置激活态的颜色。至于大小,到目前为止,Checkbox的大小是固定的,无法自定义,而Switch只能定义宽度,高度也是固定的。值得一提的是Checkbox有一个属性tristate ,表示是否为三态,其默认值为false ,这时Checkbox有两种状态即“选中”和“不选中”,对应的value值为true和false ;如果其值为true时,value的值会增加一个状态null,读者可以自行了解。

6.Radio

在项目开发或者真实案例中Radio通常都是成组出现,比如性别选择、爱好选择等等场景中,Flutter充分考虑到了这一场景需求,给我们提供了便捷使用Radio的API。利用flutter的Radio能满足我们在原生Android中各个使用Radio的场景。下面我们来一起看下构造方法,顺便有一点小细节需要讲解一下。

  1. const Radio({
  2. Key key,
  3. @required this.value,
  4. @required this.groupValue,
  5. @required this.onChanged,
  6. this.activeColor,
  7. this.materialTapTargetSize,
  8. })

看下Radio中的这段代码

  1. final T value;
  2. final T groupValue;

从Radio的这段源码注释中我们可以看到Radio在处理分组时,valuegroupValue是作为一个泛型参数,也就是说我们可以指定value的类型为int、或者String等类型,并且当value和groupValue一致的时候则选中

根据这些我们就可以自由发挥去处理我们实际开发中需要使用Radio分组的业务逻辑了。下面我贴上一个选择性别选择数字的样例代码供大家参考,读者可结合上述关于Radio的源码分析和下面的代码对比学习。

  1. import 'package:flutter/material.dart';
  2. void main() {
  3. runApp(new MaterialApp(home: new FlutterDemo()));
  4. }
  5. class FlutterDemo extends StatefulWidget {
  6. @override
  7. State<StatefulWidget> createState() => SliderState();
  8. }
  9. class SliderState extends State {
  10. int _value = 0;
  11. String _sex = '男';
  12. void _onRadioChanged(int value) {
  13. setState(() {
  14. _value = value;
  15. });
  16. }
  17. void _onSexRadioChanged(String value) {
  18. setState(() {
  19. _sex = value;
  20. });
  21. }
  22. @override
  23. Widget build(BuildContext context) {
  24. return new Scaffold(
  25. appBar: new AppBar(
  26. title: new Text("Flutter进阶之旅"),
  27. ),
  28. body: new Center(
  29. child: Column(children: <Widget>[
  30. Radio(
  31. value: 0,
  32. groupValue: _value,
  33. onChanged: _onRadioChanged,
  34. ),
  35. Radio(
  36. value: 1,
  37. groupValue: _value,
  38. onChanged: _onRadioChanged,
  39. ),
  40. Radio(
  41. value: 2,
  42. groupValue: _value,
  43. onChanged: _onRadioChanged,
  44. ),
  45. Text("选择的数字为$_value"),
  46. SizedBox(height: 50.0),
  47. Text("选择性别"),
  48. new Row(
  49. mainAxisAlignment: MainAxisAlignment.center,
  50. children: <Widget>[
  51. Radio(
  52. value: '男',
  53. groupValue: _sex,
  54. onChanged: _onSexRadioChanged,
  55. ),
  56. Radio(
  57. value: '女',
  58. groupValue: _sex,
  59. onChanged: _onSexRadioChanged,
  60. ),
  61. ],
  62. ),
  63. Text(_sex),
  64. ]),
  65. ),
  66. );
  67. }
  68. }

效果图
11- Flutter入门进阶之旅-Index-Chose - 图7