Flutter: 轮播组件

实现一个轮播

在PC端实现轮播一般都是直接使用JavaScript库进行实现.但在Flutter上想要实现一个轮播,可能只有自己写一下了.

轮播的特点:

1. 可以自动播放;
2. 图片可以左右滑动(使用Flutter自带的PageView初始只能向右滑动,不能向左);
3. 可以定义自动播放的间隔时间.

大概就这么几个特点,可以实现个简单的例子.

image-3203

功能部分

目前仅实现简单功能:
1. 可以自定义网络图片或本地图片(可以混用本地图片和网络图片),
2. 自定义图片切换时间,
3. 自定义是否开启自动轮播.

上述几条可以满足一个简单轮播的需要,如果需要更多功能.可以在此基础上增加.

image-3204

源码

完整代码: Gitee仓库


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/// 轮播组件
///
///  目前仅实现简单功能:
///   1. 可以自定义网络图片或本地图片(可以混用本地图片和网络图片),
///   2. 自定义图片切换时间,
///   3. 自定义是否开启自动轮播.
///
///  调用示例:
///
///  ```dart
///  static const List<String> _images = [
///   'assets/images/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
///   'assets/images/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',
///   'https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg',
///   'https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg',
///   'https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg',
///   'https://fuss10.elemecdn.com/3/28/bbf893f792f03a54408b3b7a7ebf0jpeg.jpeg',
///   'https://fuss10.elemecdn.com/2/11/6535bcfb26e4c79b48ddde44f4b6fjpeg.jpeg',
///   'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
///   'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',
///   'https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg'
///  ];
///
/// ......
///
/// SizedBox(
//            width: double.infinity,
//            height: 200,
//            child: Carousel(
//              _images,
//              autoPlayer: false,
//            ),
//          ),
///
/// ```
///
///
///
///
class Carousel extends StatefulWidget {
  Carousel(
    this.imagesList, {
    Key key,
    this.autoPlayer = true,
    this.seconds = 5,
  })  : assert(imagesList.isNotEmpty),
        assert(seconds > 2),
        super(key: key);

  final int _maxPage = 2000000000;

  /// 轮播切换间隔秒数.
  final int seconds;

  /// 是否自动播放
  final bool autoPlayer;

  final List<String> imagesList;

  @override
  _CarouselState createState() => new _CarouselState();
}

class _CarouselState extends State<Carousel> {

  PageController _pageController;
  Timer _timer;

  var _currIndex = 0;

  @override
  void initState() {
    super.initState();
    if (widget.autoPlayer) {
      startTimeout();
    }
    _pageController = new PageController(initialPage: widget._maxPage);
  }

  @override
  void dispose() {
    super.dispose();
    _pageController.dispose();
    if (_timer != null) {
      _timer.cancel();
    }

  }

  void startTimeout() {
    var timeout = Duration(seconds: widget.seconds);
    Timer.periodic(timeout, (Timer timer) {
      var tempIndex = _currIndex + 1;
      if (tempIndex >= widget.imagesList.length) {
        tempIndex = 0;
      }
      setState(() {
        _timer = timer;
        _currIndex = tempIndex;
      });
    });
  }

  List<Widget> getBottomIcon() {
    var forLength = widget.imagesList.length;
    var res = List<Widget>();
    for (var i = 0; i < forLength; i++) {
      if (_currIndex == i) {
        res.add(GestureDetector(
          /// 选中时的图标,可以更换为图片.
          child: Icon(Icons.brightness_1),
          onTap: () {
            setState(() {
              _currIndex = i;
            });
          },
        ));
      } else {
        res.add(GestureDetector(
          /// 未选中时的图标,可以更换为图片.
          child: Icon(Icons.panorama_fish_eye),
          onTap: () {
            setState(() {
              _currIndex = i;
            });
          },
        ));
      }
    }
    return res;
  }

  @override
  Widget build(BuildContext context) {
    var width = MediaQuery.of(context).size.width;
    return Scaffold(
      body: PageView.builder(
          controller: _pageController,
          onPageChanged: (int index) {
            var renderIndex = index - widget._maxPage;
            renderIndex = renderIndex % widget.imagesList.length;
            if (renderIndex < 0) {
              renderIndex += widget.imagesList.length;
            }
            setState(() {
              _currIndex = renderIndex;
            });
          },
          itemBuilder: (BuildContext context, int index) {
            var renderIndex = _currIndex - widget._maxPage;
            renderIndex = renderIndex % widget.imagesList.length;
            if (renderIndex < 0) {
              renderIndex += widget.imagesList.length;
            }
            return Container(
                child: Stack(children: <Widget>[
              Center(
                child: widget.imagesList[_currIndex].startsWith("https://") ||
                        widget.imagesList[_currIndex].startsWith("http://")
                    ? CachedNetworkImage(
                        imageUrl: widget.imagesList[_currIndex],
                        fit: BoxFit.fill,
                        width: width,
                        height: double.infinity,
                        placeholder: (context, url) =>
                            CircularProgressIndicator(),
                        errorWidget: (context, url, error) => Icon(Icons.error),
                      )
                    : FadeInImage(
                        image: AssetImage(widget.imagesList[_currIndex]),
                        placeholder: AssetImage("assets/images/loading.gif"),
                        fit: BoxFit.fill,
                        width: width,
                        height: double.infinity,
                      ),
              ),
              Positioned(
                  left: 2.0,
                  right: 2.0,
                  bottom: 10.0,
                  child: Container(

                      /// padding: EdgeInsets.all(5.0),
                      child: widget.imagesList.length > 10
                          ? SingleChildScrollView(
                              scrollDirection: Axis.horizontal,
                              child: Row(
                                mainAxisAlignment: MainAxisAlignment.center,
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: getBottomIcon(),
                              ),
                            )
                          : Row(
                              mainAxisAlignment: MainAxisAlignment.center,
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: getBottomIcon(),
                            )))
            ]));
          }),
    );
  }
}