[Flutter]flutter基础之组件基础(十八)

一、ProgressIndicator Widget

ProgressIndicator 是进度指示器的抽象基类,不能直接被实例化。构造方法如下:

1
const ProgressIndicator({
2
  Key key,
3
  //double类型可选命名参数,如果不为空,则此进度指示器的值
4
  this.value,
5
  //Color类型可选命名参数,进度指示器的背景色
6
  this.backgroundColor,
7
  //Animation<Color>类型可选命名参数,使用动画值作为进度指示器的进度颜色
8
  this.valueColor,
9
  //String类型可选命名参数,此进度指示器的Semantics.label
10
  this.semanticsLabel,
11
  //String类型可选命名参数,此进度指示器的Semantics.value
12
  this.semanticsValue,
13
})

自身其有两个实现类,分别为:LinearProgressIndicatorCircularProgressIndicator ,当然也可以自定义。

1. LinearProgressIndicator Widget

LinearProgressIndicator 是一个线性进度指示器 Widget ,也就是一个进度条。构造方法如下:

1
const LinearProgressIndicator({
2
  Key key,
3
  //double类型可选命名参数,如果不为空,则此进度指示器的值
4
  double value,
5
  //Color类型可选命名参数,进度指示器的背景色
6
  Color backgroundColor,
7
  //Animation<Color>类型可选命名参数,进度指示器的颜色作为动画值
8
  Animation<Color> valueColor,
9
  //String类型可选命名参数,此进度指示器的Semantics.label
10
  String semanticsLabel,
11
  //String类型可选命名参数,此进度指示器的Semantics.value
12
  String semanticsValue,
13
})

LinearProgressIndicator 可以由两种表现形式。当 valuenull 时,进度条是一个线性的循环模式,表示正在加载,但是不能确定已完成和未完成的比例。当 value 不为空时,可设置 0.0 到 1.0 之间的非空值,不能值表示不同的加载进度。

valueColorAnimation<Color> 类型,是一个动画类,当不需要使用动画,将进度值设置为固定颜色时,可以使用 AlwaysStoppedAnimation<Color>(color)AlwaysStoppedAnimation 也是动画的一个实现类,表示一个固定的值。后续文章的动画部分会做详细讲解。

LinearProgressIndicator 使用如下:

1
import 'package:flutter/material.dart';
2
import 'package:flutter/cupertino.dart';
3
4
void main() => runApp(MyApp());
5
6
class MyApp extends StatelessWidget {
7
  @override
8
  Widget build(BuildContext context) {
9
    return MaterialApp(
10
      home: MyHomePage(),
11
    );
12
  }
13
}
14
15
class MyHomePage extends StatefulWidget {
16
  @override
17
  _MyHomePageState createState() => _MyHomePageState();
18
}
19
20
class _MyHomePageState extends State<MyHomePage> {
21
  double currentValue = 0.3;
22
  @override
23
  Widget build(BuildContext context) {
24
    return Scaffold(
25
      appBar: AppBar(
26
        backgroundColor: Colors.blue,
27
        title: Text("HomePage"),
28
      ),
29
      body:Center(
30
        child: Container(
31
            width: 200,
32
            height: 10,
33
            child: LinearProgressIndicator(     //LinearProgressIndicator
34
              value: currentValue,
35
              backgroundColor: Colors.grey,
36
              valueColor: AlwaysStoppedAnimation(Colors.red),
37
            ),
38
        ),
39
      ),
40
    );
41
  }
42
}

效果如下(左侧为 valuenull 效果,右侧为非 null 效果):

20204116292020411630

2. CircularProgressIndicator Widget

CircularProgressIndicator 是循环进度指示器,是使用圆圈显示进度的 Widget 。使用方法与上述的线性进度指示器相同,其构造方法如下:

1
const CircularProgressIndicator({
2
  Key key,
3
  //double类型可选命名参数,如果不为空,则此进度指示器的值
4
  double value,
5
  //Color类型可选命名参数,进度指示器的背景色
6
  Color backgroundColor,
7
  //Animation<Color>类型可选命名参数,使用动画值作为进度指示器的进度颜色
8
  Animation<Color> valueColor,
9
  //double类型可选命名参数,用于画圆的线的宽度
10
  this.strokeWidth = 4.0,
11
  //String类型可选命名参数,此进度指示器的Semantics.label
12
  String semanticsLabel,
13
  //String类型可选命名参数,此进度指示器的Semantics.value
14
  String semanticsValue,
15
})

使用如下:

1
class _MyHomePageState extends State<MyHomePage> {
2
  double currentValue = 0.3;
3
  @override
4
  Widget build(BuildContext context) {
5
    return Scaffold(
6
      appBar: AppBar(
7
        backgroundColor: Colors.blue,
8
        title: Text("HomePage"),
9
      ),
10
      body:Center(
11
        child: Container(
12
            width: 300,
13
            height: 300,
14
            child: CircularProgressIndicator(
15
              value: currentValue,
16
              strokeWidth: 10,
17
              backgroundColor: Colors.grey,
18
              valueColor: AlwaysStoppedAnimation(Colors.red),
19
            ),
20
        ),
21
      ),
22
    );
23
  }
24
}

效果如下:

2020411638

二、RefreshIndicator Widget

RefreshIndicator 是 Material 风格的滑动刷新 Widget ,效果是下拉刷新。当滑动距离超过设置的滑动距离后,一个动态循环指示器会淡入视图。当 Scrollable 没有足够的内容来进行滚动操作时,可将其 physics 属性设置为 AlwaysScrollableScrollPhysics 。使用 AlwaysScrollableScrollPhysics 将确保滚动视图始终可滚动,因此可以触发 RefreshIndicatorRefreshIndicator 只能与垂直滚动视图一起使用。

构造方法如下:

1
const RefreshIndicator({
2
  Key key,
3
  //Widget类型必传参数,要显示的Widget,通常为ListView或CustomScrollView
4
  @required this.child,
5
  //double类型可选命名参数,从子Widget的顶部或底部边缘到刷新指示符所在位置的距离
6
  //在公开刷新指示器的拖动过程中,其实际位移可能会大大超过此值。
7
  this.displacement = 40.0,
8
  //RefreshCallback类型必传参数,当用户将刷新指示器拖到足够远以表明他们希望应用刷新时调用的函数。
9
  //刷新操作完成后,返回的Future必须完成
10
  @required this.onRefresh,
11
  //Color类型可选命名参数,进度指示器前景色。默认情况下,当前主题ThemeData.accent颜色
12
  this.color,
13
  //Color类型可选命名参数,进度指示器背景色。默认情况下,当前主题ThemeData.canvas颜色。
14
  this.backgroundColor,
15
  //ScrollNotificationPredicate类型可选命名参数,一个检查,指定此窗口小部件是否应
16
  //处理ScrollNotification
17
  this.notificationPredicate = defaultScrollNotificationPredicate,
18
  //String类型可选命名参数,此进度指示器的Semantics.label
19
  this.semanticsLabel,
20
  //String类型可选命名参数,此进度指示器的Semantics.value
21
  this.semanticsValue,
22
})

onRefreshRefreshCallback 类型,是一个返回值为 Future<void> 类型的方法,定义为:Future<void> Function(); 。这个方法当用户拖动一个刷新指示器足够远以显示他们希望应用程序刷新时调用。

下拉刷新使用如下:

1
class _MyHomePageState extends State<MyHomePage> {
2
  @override
3
  Widget build(BuildContext context) {
4
    return Scaffold(
5
      appBar: AppBar(
6
        backgroundColor: Colors.blue,
7
        title: Text("HomePage"),
8
      ),
9
      body:Center(
10
        child: Container(
11
            child: RefreshIndicator(
12
              child: ListView(
13
                scrollDirection: Axis.vertical,  
14
                padding: EdgeInsets.all(10),    
15
                itemExtent: 200,        
16
                children: <Widget>[
17
                  Container(
18
                    color: Colors.lightBlueAccent,
19
                    height: 120,
20
                    width: 160,
21
                    child: const Center(child: Text("Text 1"),),
22
                  ),
23
                  Container(
24
                    color: Colors.yellow,
25
                    height: 120,
26
                    width: 160,
27
                    child: const Center(child: Text("Text 2"),),
28
                  ),
29
                  Container(
30
                    color: Colors.red,
31
                    height: 120,
32
                    width: 160,
33
                    child: const Center(child: Text("Text 3"),),
34
                  ),
35
                ],
36
              ),
37
              onRefresh: (){
38
                return Future.delayed(Duration(seconds: 2));
39
              },
40
            ),
41
        ),
42
      ),
43
    );
44
  }
45
}

效果如下:

2020417533

在实例开发中,下拉刷新经常与上拉加载更多一起使用。实现加载更多,可以通过使用 ScrollController 监听视图滚动来实现。ScrollController 在后续文章会详细说明。

实现如下:

1
class _MyHomePageState extends State<MyHomePage> {
2
  ScrollController _scrollController;
3
  List<String> _dataList = List();
4
  int _pageIndex = 1;
5
  bool _isLoadingFinished = true;
6
7
  @override
8
  void initState() {
9
    super.initState();
10
    _getInitData();
11
    _scrollController = ScrollController()
12
      ..addListener((){   //添加监听
13
        if(_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
14
          _getMoreData();
15
        }
16
      });
17
  }
18
19
  //初始化第一页数据
20
  Future _getInitData() async {
21
    _pageIndex = 1;
22
    _dataList.removeRange(0, _dataList.length);
23
    await Future.delayed(Duration(seconds: 1),(){
24
      setState(() {
25
        _dataList = List.generate(10, (int i){return "获取的数据列表数据 $i";});
26
27
      });
28
    });
29
  }
30
31
  //获取更多数据
32
  Future _getMoreData() async {
33
    if(_isLoadingFinished) {
34
      _isLoadingFinished = false;
35
      await Future.delayed(Duration(seconds: 1), (){
36
        setState(() {
37
          _dataList.addAll(List.generate(5, (int i){
38
            return "这是新的数据 $i";
39
          }));
40
          _isLoadingFinished = true;
41
        });
42
      });
43
    }
44
  }
45
46
  @override
47
  Widget build(BuildContext context) {
48
    return Scaffold(
49
      appBar: AppBar(
50
        backgroundColor: Colors.blue,
51
        title: Text("HomePage"),
52
      ),
53
      body:Center(
54
        child: Container(
55
          child: RefreshIndicator(
56
            child: ListView.builder(
57
              controller: _scrollController,
58
              scrollDirection: Axis.vertical,
59
              padding: EdgeInsets.all(10),
60
              itemExtent: 100,
61
              itemBuilder: _renderListRow,
62
              itemCount: _dataList.length == 0 ? _dataList.length : _dataList.length+1,
63
            ),
64
            onRefresh: (){
65
              return _getInitData();
66
            },
67
          ),
68
        ),
69
      ),
70
    );
71
  }
72
 
73
  //渲染加载数据
74
  Widget _renderListRow(BuildContext context, int index) {
75
    if(index < _dataList.length){
76
      return Container(
77
        height: 100,
78
        child: Text(_dataList[index] ),
79
      );
80
    }
81
    return Center(
82
      child: Row(
83
        mainAxisAlignment: MainAxisAlignment.center,
84
        children: <Widget>[
85
          Text("加载更多", style: TextStyle(fontSize: 20),),
86
          Container(
87
            width: 15,
88
            height: 15,
89
            margin: EdgeInsets.only(left: 10),
90
            child: CircularProgressIndicator(strokeWidth: 2,),
91
          ),
92
        ],
93
      ),
94
    );
95
  }
96
}

效果如下:

2020419330

三、RefreshProgressIndicator Widget

RefreshProgressIndicator 是用于显示刷新小部件内容的进度指示器,其继承自 CircularProgressIndicator 。通常用于与刷新相关的进度显示。其默认样式与 RefreshIndicator 下拉后显示的圆形指示器相同。构造方法如下:

1
const RefreshProgressIndicator({
2
  Key key,
3
  //double类型可选命名参数,进度指示器的值
4
  double value,
5
  //Color类型可选命名参数,背景色
6
  Color backgroundColor,
7
  //Animation<Color>类型可选命名参数,使用动画值作为进度指示器的进度颜色
8
  Animation<Color> valueColor,
9
  //double类型可选命名参数,用于画圆的线的宽度
10
  double strokeWidth = 2.0, // Different default than CircularProgressIndicator.
11
  //String类型可选命名参数,此进度指示器的Semantics.label
12
  String semanticsLabel,
13
  //String类型可选命名参数,此进度指示器的Semantics.value
14
  String semanticsValue,
15
})

使用如下:

1
class _MyHomePageState extends State<MyHomePage> {
2
  @override
3
  Widget build(BuildContext context) {
4
    return Scaffold(
5
      appBar: AppBar(
6
        backgroundColor: Colors.blue,
7
        title: Text("HomePage"),
8
      ),
9
      body:Center(
10
        child: Container(
11
          height: 300,
12
          width: 300,
13
          child: RefreshProgressIndicator(
14
            value: 0.3,
15
            strokeWidth: 10,
16
            backgroundColor: Colors.yellow,
17
            valueColor: AlwaysStoppedAnimation<Color>(Colors.red),
18
          ),
19
        ),
20
      ),
21
    );
22
  }
23
}

效果如下:

20204171024

四、ListWheelScrollView Widget

ListWheelScrollView 是一个可滚动的轮子式的 Widget ,子级的渲染就像在轮子上旋转而不是在平面上滚动一样。与 ListView 类似,但其所有子 Widget 在滚动轴上必须具有相同的大小。当列表的滚动偏移量为 0 时,第一个子 Widget 与视口中间对齐。 当列表位于最终滚动偏移处时,最后一个子 Widget 与视口的中间对齐。

构造方法如下:

1
//创建一个ListWheelScrollView,它的子级被传递给委托人,并在布局期间延迟构建
2
ListWheelScrollView({
3
  Key key,
4
  //ScrollController类型可选命名参数,控制器,通常使用FixedExtentScrollController来控制当前项目
5
  this.controller,
6
  //ScrollPhysics类型可选命名参数,滚动视图应如何响应用户输入
7
  this.physics,
8
  //double类型可选命名参数,圆柱体直径与主轴上视口大小之间的比率
9
  this.diameterRatio = RenderListWheelViewport.defaultDiameterRatio,
10
  //double类型可选命名参数,圆柱投影的透视图
11
  this.perspective = RenderListWheelViewport.defaultPerspective,
12
  //double类型可选命名参数,轮子水平偏离中心的程度,即为其宽度的一小部分。这一属性创造了一种视觉效果,
13
  //从它的侧面看一个垂直的轮子,它的消失点在边缘曲线的一边,而不是正面看轮子
14
  this.offAxisFraction = 0.0,
15
  //bool类型可选命名参数,是否将放大镜用于轮子的中心项目
16
  this.useMagnifier = false,
17
  //bool类型可选命名参数,如果使用了放大镜,则放大率
18
  this.magnification = 1.0,
19
  //double类型必传参数,主轴上每个子项的大小。不能为null,并且必须为正
20
  @required this.itemExtent,
21
  //double类型可选命名参数,轮子上子Widget的角度紧凑度
22
  this.squeeze = 1.0,
23
  //ValueChanged<int>类型可选命名参数,在中心项更改时调用的可选侦听器上
24
  this.onSelectedItemChanged,
25
  //bool类型可选命名参数,是否将绘制的子项剪辑到此视口的内部
26
  this.clipToSize = true,
27
  //bool类型可选命名参数,是否仅在视口内绘制子代.如果clipToSize也为true,则不能为true,
28
  //因为视口外部的子代将被裁剪,因此无法在视口之外呈现子代。
29
  this.renderChildrenOutsideViewport = false,
30
  //List<Widget>类型必传参数,子Widget列表
31
  @required List<Widget> children,
32
})
33
  
34
//创建一个ListWheelScrollView,它的子级由一名代表管理,并且在布局过程中延迟构建
35
const ListWheelScrollView.useDelegate({
36
  Key key,
37
  //ScrollController类型可选命名参数,控制器,通常使用FixedExtentScrollController来控制当前项目
38
  this.controller,
39
  //ScrollPhysics类型可选命名参数,滚动视图应如何响应用户输入
40
  this.physics,
41
  //double类型可选命名参数,圆柱体直径与主轴上视口大小之间的比率
42
  this.diameterRatio = RenderListWheelViewport.defaultDiameterRatio,
43
  //double类型可选命名参数,圆柱投影的透视图
44
  this.perspective = RenderListWheelViewport.defaultPerspective,
45
  //double类型可选命名参数,轮子水平偏离中心的程度,即为其宽度的一小部分。这一属性创造了一种视觉效果,
46
  //从它的侧面看一个垂直的轮子,它的消失点在边缘曲线的一边,而不是正面看轮子
47
  this.offAxisFraction = 0.0,
48
  //bool类型可选命名参数,是否将放大镜用于轮子的中心项目
49
  this.useMagnifier = false,
50
  //bool类型可选命名参数,如果使用了放大镜,则放大率
51
  this.magnification = 1.0,
52
  //double类型必传参数,主轴上每个子项的大小。不能为null,并且必须为正
53
  @required this.itemExtent,
54
  //double类型可选命名参数,轮子上子Widget的角度紧凑度,每个item的距离
55
  this.squeeze = 1.0,
56
  //ValueChanged<int>类型可选命名参数,在中心项更改时调用的可选侦听器上,是一个回调方法
57
  this.onSelectedItemChanged,
58
  //bool类型可选命名参数,是否将绘制的子项剪辑到此视口的内部
59
  this.clipToSize = true,
60
  //bool类型可选命名参数,是否仅在视口内绘制子代
61
  this.renderChildrenOutsideViewport = false,
62
  //ListWheelChildDelegate类型必传参数,惰性代理
63
  @required this.childDelegate,
64
})

其中 onSelectedItemChangedValueChanged<int> 类型,是一个无返回值的方法,定义为:void Function(T value);

childDelegateListWheelChildDelegate 类型,为 ListWheelScrollView 提供子级的委托。 ListWheelScrollView 在布局期间懒惰地构造其子级,以避免创建比通过视口可见的子级更多的子级。该委托负责在此阶段为 ListWheelScrollView 提供子级。ListWheelChildDelegate 是一个抽象类,提供了两个子类可以使用:ListWheelChildBuilderDelegateListWheelChildLoopingListDelegate

ListWheelChildBuilderDelegate 使用构建器回调为 ListWheelScrollView 提供子级的委托。其惰性地构造其子级,以避免创建的子级超过通过视口可见的子级。 该委托使用 IndexedWidgetBuilder 回调提供子项,因此不必在显示子项之前对其进行构建。构造方法如下:

1
ListWheelChildBuilderDelegate({
2
  //IndexedWidgetBuilder类型必传参数,惰性创建子Widget
3
  @required this.builder,
4
  //int类型可选命名参数,如果非null,则childCount是可提供的最大子代数,并且子代的可用
5
  //范围是0到childCount-1
6
  this.childCount,
7
})

builderIndexedWidgetBuilder 类型,是一个返回值为 Widget 的方法,定义为:Widget Function(BuildContext context, int index);

ListWheelChildLoopingListDelegate 是通过循环显示列表的 ListWheelScrollView 委托。此类适用于子项列表已知的情况。构造方法如下:

1
ListWheelChildLoopingListDelegate({
2
  //List<Widget>类型必传参数,该列表包含可以提供的所有子Widget
3
  @required this.children
4
})

ListWheelScrollView 使用如下:

1
class _MyHomePageState extends State<MyHomePage> {
2
  @override
3
  Widget build(BuildContext context) {
4
    return Scaffold(
5
      appBar: AppBar(
6
        backgroundColor: Colors.blue,
7
        title: Text("HomePage"),
8
      ),
9
      body:Center(
10
        child: Container(
11
          height: 300,
12
          color: Colors.yellow,
13
          child: ListWheelScrollView(
14
            itemExtent: 100,
15
            onSelectedItemChanged: (int value){
16
              print(value);
17
            },
18
            children: <Widget>[
19
              Container(
20
                color: Colors.red,
21
                height: 120,
22
                width: 160,
23
                child: const Center(child: Text("Text 1"),),
24
              ),
25
              Container(
26
                color: Colors.red,
27
                height: 120,
28
                width: 160,
29
                child: const Center(child: Text("Text 2"),),
30
              ),
31
              Container(
32
                color: Colors.red,
33
                height: 120,
34
                width: 160,
35
                child: const Center(child: Text("Text 3"),),
36
              ),
37
              Container(
38
                color: Colors.red,
39
                height: 120,
40
                width: 160,
41
                child: const Center(child: Text("Text 4"),),
42
              ),
43
            ],
44
          ),
45
        ),
46
      ),
47
    );
48
  }
49
}

效果如下:

20204181111

ListWheelScrollView.useDelegate 使用 ListWheelChildBuilderDelegate 如下:

1
class _MyHomePageState extends State<MyHomePage> {
2
  List _itemList = ["Text 1", "Text2", "Text3", "Text4"];
3
  @override
4
  Widget build(BuildContext context) {
5
    return Scaffold(
6
      appBar: AppBar(
7
        backgroundColor: Colors.blue,
8
        title: Text("HomePage"),
9
      ),
10
      body:Container(
11
        height: 300,
12
        color: Colors.yellow,
13
        child: ListWheelScrollView.useDelegate (
14
          itemExtent: 100,
15
          onSelectedItemChanged: (int value){
16
            print(value);
17
          },
18
          childDelegate: ListWheelChildBuilderDelegate(
19
            builder: (BuildContext context, int index) {
20
              return Container(
21
                  color: Colors.red,
22
                  height: 120,
23
                  width: 160,
24
                  child: Center(child: Text("${_itemList[index]}", style: TextStyle(fontSize: 30),)),
25
              );
26
            },
27
            childCount: _itemList.length,
28
          ),
29
        ),
30
      ),
31
    );
32
  }
33
}

效果如下:

20204181251

ListWheelScrollView.useDelegate 使用 ListWheelChildBuilderDelegate 如下:

1
class _MyHomePageState extends State<MyHomePage> {
2
  @override
3
  Widget build(BuildContext context) {
4
    return Scaffold(
5
      appBar: AppBar(
6
        backgroundColor: Colors.blue,
7
        title: Text("HomePage"),
8
      ),
9
      body:Container(
10
        height: 300,
11
        color: Colors.yellow,
12
        child: ListWheelScrollView.useDelegate (
13
          itemExtent: 100,
14
          onSelectedItemChanged: (int value){
15
            print(value);
16
          },
17
          childDelegate: ListWheelChildLoopingListDelegate(
18
            children: [
19
              Container(
20
                color: Colors.red,
21
                height: 120,
22
                width: 160,
23
                child: Center(child: Text("Text1", style: TextStyle(fontSize: 30),)),
24
              ),
25
              Container(
26
                color: Colors.red,
27
                height: 120,
28
                width: 160,
29
                child: Center(child: Text("Text2", style: TextStyle(fontSize: 30),)),
30
              ),
31
            ],
32
          ),
33
        ),
34
      ),
35
    );
36
  }
37
}

效果如下:

20204181256
陌问.MW wechat
欢迎关注微信公众号,及时获取知识!