├── banner_gallery_library ├── LICENSE ├── CHANGELOG.md ├── .gitignore ├── .flutter-plugins ├── lib │ ├── bean │ │ └── banner_gallery_bean.dart │ ├── page_view_indicator.dart │ └── banner_gallery_library.dart ├── .idea │ ├── modules.xml │ ├── libraries │ │ ├── Flutter_Plugins.xml │ │ ├── Dart_SDK.xml │ │ └── Dart_Packages.xml │ ├── misc.xml │ ├── inspectionProfiles │ │ └── Project_Default.xml │ ├── codeStyles │ │ └── Project.xml │ └── workspace.xml ├── README.md ├── test │ └── banner_gallery_library_test.dart ├── banner_gallery_library.iml └── pubspec.yaml ├── photos └── Screenshot_v1.gif ├── .gitignore └── README.md /banner_gallery_library/LICENSE: -------------------------------------------------------------------------------- 1 | TODO: Add your license here. 2 | -------------------------------------------------------------------------------- /photos/Screenshot_v1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3clikejava/BannerGalleryInFlutter/HEAD/photos/Screenshot_v1.gif -------------------------------------------------------------------------------- /banner_gallery_library/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [0.0.1] - TODO: Add release date. 2 | 3 | * TODO: Describe initial release. 4 | -------------------------------------------------------------------------------- /banner_gallery_library/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | ios/.generated/ 9 | ios/Flutter/Generated.xcconfig 10 | ios/Runner/GeneratedPluginRegistrant.* 11 | -------------------------------------------------------------------------------- /banner_gallery_library/.flutter-plugins: -------------------------------------------------------------------------------- 1 | path_provider=/Users/H3c/.pub-cache/hosted/pub.flutter-io.cn/path_provider-0.4.1/ 2 | shared_preferences=/Users/H3c/.pub-cache/hosted/pub.flutter-io.cn/shared_preferences-0.4.2/ 3 | -------------------------------------------------------------------------------- /banner_gallery_library/lib/bean/banner_gallery_bean.dart: -------------------------------------------------------------------------------- 1 | class BannerGalleryBean { 2 | BannerGalleryBean({this.id, this.photoUrl, this.description}); 3 | 4 | /// 唯一标识 5 | final String id; 6 | 7 | /// 图片网址 8 | final String photoUrl; 9 | 10 | /// 描述 11 | final String description; 12 | } 13 | -------------------------------------------------------------------------------- /banner_gallery_library/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /banner_gallery_library/README.md: -------------------------------------------------------------------------------- 1 | # banner_gallery_library 2 | 3 | It's an infinity banner gallery widget in flutter. 4 | 5 | ## Getting Started 6 | 7 | For help getting started with Flutter, view our online [documentation](https://flutter.io/). 8 | 9 | For help on editing package code, view the [documentation](https://flutter.io/developing-packages/). 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://www.dartlang.org/guides/libraries/private-files 2 | 3 | # Files and directories created by pub 4 | .dart_tool/ 5 | .packages 6 | .pub/ 7 | build/ 8 | # If you're building an application, you may want to check-in your pubspec.lock 9 | pubspec.lock 10 | 11 | # Directory created by dartdoc 12 | # If you don't generate documentation locally you can remove this line. 13 | doc/api/ 14 | .DS_Store 15 | -------------------------------------------------------------------------------- /banner_gallery_library/.idea/libraries/Flutter_Plugins.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /banner_gallery_library/test/banner_gallery_library_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:test/test.dart'; 2 | 3 | import 'package:banner_gallery_library/banner_gallery_library.dart'; 4 | 5 | void main() { 6 | test('adds one to input values', () { 7 | final calculator = new Calculator(); 8 | expect(calculator.addOne(2), 3); 9 | expect(calculator.addOne(-7), -6); 10 | expect(calculator.addOne(0), 1); 11 | expect(() => calculator.addOne(null), throwsNoSuchMethodError); 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /banner_gallery_library/banner_gallery_library.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /banner_gallery_library/.idea/libraries/Dart_SDK.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /banner_gallery_library/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BannerGalleryInFlutter 2 | 用Flutter实现的无限滑动Banner。 3 | 4 | ![Screenshot](https://github.com/h3clikejava/BannerGalleryInFlutter/blob/master/photos/Screenshot_v1.gif?raw=true) 5 | 6 | 可配置的属性 7 | 8 | |参数名|参数说明| 9 | |-----|--------| 10 | |customViewPageItemWidget|自定义内容布局| 11 | |autoScrollDurationSeconds|自动滑动时间间隔[单位s]| 12 | |bannerScrollDirection|Banner滑动方向[水平/垂直]| 13 | |bannerMargin|Banner之间间距| 14 | |bannerBorderRadius|Banner圆角| 15 | |bannerDefaultBGColor|Banner默认背景颜色| 16 | |bannerTextAlignment|Banner文字位置| 17 | |bannerTextPadding|Banner文字Padding| 18 | |bannerTextColor|Banner文字颜色| 19 | |bannerTextBGColor|Banner文字背景颜色| 20 | |onPageTap|点击的事件监听| 21 | |height|View高度| 22 | |indicatorPositioned|指示器位置| 23 | |indicatorScrollDirection|指示器方向 [水平/垂直]| 24 | |indicatorNormalColor|指示器默认颜色| 25 | |indicatorSelectedColor|指示器选中颜色| 26 | |indicatorNormalSize|指示器点默认大小| 27 | |indicatorScaleSize|指示器点选中放大倍数,默认1.4倍| 28 | |indicatorSpacing|指示器点的间距| 29 | |indicatorStyle|指示器样式[circle: 圆形, square: 方形]| 30 | |indicatorAnimStyle|指示器动画样式[normal: 选中变色, scaled:选中放大]| 31 | 32 | 33 | 使用示例 34 | 35 | ``` 36 | /// 构建数据 37 | List _createTestData() { 38 | List list = new List(); 39 | for (int n = 0; n < IMGS.length; n++) { 40 | list.add(BannerGalleryBean( 41 | id: n.toString(), 42 | photoUrl: https://www.baidu.com/img/bd_logo1.png?where=super, 43 | description: n.toString); 44 | } 45 | return list; 46 | } 47 | 48 | /// 构建Widget 49 | BannerGalleryWidget( 50 | data: _createTestData(), 51 | indicatorSelectedColor: Theme.of(context).primaryColor, 52 | ) 53 | ``` 54 | -------------------------------------------------------------------------------- /banner_gallery_library/.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 36 | -------------------------------------------------------------------------------- /banner_gallery_library/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: banner_gallery_library 2 | description: It's an infinity banner gallery widget in flutter. 3 | version: 0.0.1 4 | author: H3c 5 | homepage: https://github.com/h3clikejava/BannerGalleryInFlutter 6 | 7 | dependencies: 8 | flutter: 9 | sdk: flutter 10 | 11 | cached_network_image: ^0.4.1 12 | 13 | dev_dependencies: 14 | flutter_test: 15 | sdk: flutter 16 | 17 | # For information on the generic Dart part of this file, see the 18 | # following page: https://www.dartlang.org/tools/pub/pubspec 19 | 20 | # The following section is specific to Flutter. 21 | flutter: 22 | 23 | # To add assets to your package, add an assets section, like this: 24 | # assets: 25 | # - images/a_dot_burr.jpeg 26 | # - images/a_dot_ham.jpeg 27 | # 28 | # For details regarding assets in packages, see 29 | # https://flutter.io/assets-and-images/#from-packages 30 | # 31 | # An image asset can refer to one or more resolution-specific "variants", see 32 | # https://flutter.io/assets-and-images/#resolution-aware. 33 | 34 | # To add custom fonts to your package, add a fonts section here, 35 | # in this "flutter" section. Each entry in this list should have a 36 | # "family" key with the font family name, and a "fonts" key with a 37 | # list giving the asset and other descriptors for the font. For 38 | # example: 39 | # fonts: 40 | # - family: Schyler 41 | # fonts: 42 | # - asset: fonts/Schyler-Regular.ttf 43 | # - asset: fonts/Schyler-Italic.ttf 44 | # style: italic 45 | # - family: Trajan Pro 46 | # fonts: 47 | # - asset: fonts/TrajanPro.ttf 48 | # - asset: fonts/TrajanPro_Bold.ttf 49 | # weight: 700 50 | # 51 | # For details regarding fonts in packages, see 52 | # https://flutter.io/custom-fonts/#from-packages 53 | -------------------------------------------------------------------------------- /banner_gallery_library/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /banner_gallery_library/lib/page_view_indicator.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | /// 滑动指示器的样式 6 | enum PageViewIndicatorStyle { 7 | /// 圆形 8 | circle, 9 | 10 | /// 正方形 11 | square, 12 | } 13 | 14 | /// 滑动指示器的动画样式 15 | enum PageViewIndicatorAnimStyle { 16 | /// 正常模式 17 | normal, 18 | 19 | /// 缩放模式,选中的变大 20 | scaled, 21 | } 22 | 23 | /// PageView滑动指示器 24 | class PageViewIndicator extends AnimatedWidget { 25 | PageViewIndicator({ 26 | this.controller, 27 | this.itemCount: 0, 28 | this.onPageSelected, 29 | this.scrollDirection = Axis.horizontal, 30 | this.normalColor: Colors.white, 31 | this.selectedColor: Colors.white, 32 | this.size: 8.0, 33 | this.spacing: 4.0, 34 | this.scaleSize: 1.4, 35 | this.style: PageViewIndicatorStyle.circle, 36 | this.animStyle: PageViewIndicatorAnimStyle.normal, 37 | }) : assert(controller != null), 38 | super(listenable: controller); 39 | 40 | /// PageView的控制器 41 | final PageController controller; 42 | 43 | /// 指示器的个数 44 | final int itemCount; 45 | 46 | /// 点击的事件监听 47 | final ValueChanged onPageSelected; 48 | 49 | /// 方向 50 | final Axis scrollDirection; 51 | 52 | /// 普通的颜色 53 | final Color normalColor; 54 | 55 | /// 选中的颜色 56 | final Color selectedColor; 57 | 58 | /// 点的大小 59 | final double size; 60 | 61 | /// 点的间距 62 | final double spacing; 63 | 64 | /// 点的样式 65 | final PageViewIndicatorStyle style; 66 | 67 | /// 动画样式 68 | final PageViewIndicatorAnimStyle animStyle; 69 | 70 | /// 选中放大的倍数 71 | final double scaleSize; 72 | 73 | /// 点的Widget 74 | Widget _buildIndicator(int index, int pageCount, double dotSize, 75 | double spacing, double maxZoom) { 76 | // 当前未知 77 | double currentPosition = 78 | ((controller.page ?? controller.initialPage.toDouble()) % 79 | pageCount.toDouble()); 80 | 81 | double selectedness = Curves.easeOut.transform( 82 | max( 83 | 0.0, 84 | 1.0 - (currentPosition - index).abs(), 85 | ), 86 | ); 87 | 88 | // 是否是当前页面被选中 89 | bool isCurrentPageSelected = index == 90 | (controller.page != null ? controller.page.round() % pageCount : 0); 91 | 92 | // 修复从0跳到最后一个时状态错误 93 | if (currentPosition > pageCount - 1 && index == 0) { 94 | selectedness = 1 - (pageCount.toDouble() - currentPosition); 95 | } 96 | 97 | // 计算缩放大小 98 | double zoom = 1.0 + (maxZoom - 1.0) * selectedness; 99 | 100 | // 取中间色 101 | final ColorTween selectedColorTween = 102 | ColorTween(begin: normalColor, end: selectedColor); 103 | 104 | return Container( 105 | width: size + (2 * spacing), 106 | height: size + (2 * spacing), 107 | child: Center( 108 | child: Material( 109 | shadowColor: Colors.black, 110 | elevation: 2.0, 111 | color: selectedColorTween.lerp(selectedness), 112 | type: style == PageViewIndicatorStyle.circle 113 | ? MaterialType.circle 114 | : MaterialType.button, 115 | child: Container( 116 | width: dotSize * zoom, 117 | height: dotSize * zoom, 118 | child: InkWell( 119 | onTap: () => onPageSelected(index), 120 | ), 121 | ), 122 | ), 123 | ), 124 | ); 125 | } 126 | 127 | @override 128 | Widget build(BuildContext context) { 129 | if (scrollDirection == Axis.vertical) { 130 | return Column( 131 | mainAxisAlignment: MainAxisAlignment.center, 132 | children: List.generate(itemCount, (int index) { 133 | return _buildIndicator(index, itemCount, size, spacing, 134 | animStyle == PageViewIndicatorAnimStyle.scaled ? scaleSize : 1.0); 135 | }), 136 | ); 137 | } else { 138 | return Row( 139 | mainAxisAlignment: MainAxisAlignment.center, 140 | children: List.generate(itemCount, (int index) { 141 | return _buildIndicator(index, itemCount, size, spacing, 142 | animStyle == PageViewIndicatorAnimStyle.scaled ? scaleSize : 1.0); 143 | }), 144 | ); 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /banner_gallery_library/lib/banner_gallery_library.dart: -------------------------------------------------------------------------------- 1 | library banner_gallery_library; 2 | 3 | import 'dart:async'; 4 | 5 | import 'package:cached_network_image/cached_network_image.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:banner_gallery_library/page_view_indicator.dart'; 8 | import 'package:banner_gallery_library/bean/banner_gallery_bean.dart'; 9 | 10 | // 轮播的Banner Widget 11 | class BannerGalleryWidget extends StatelessWidget { 12 | BannerGalleryWidget({ 13 | this.data, 14 | this.customViewPageItemWidget, 15 | this.autoScrollDurationSeconds = 5, 16 | this.onPageTap, 17 | this.height = 150.0, 18 | this.bannerScrollDirection = Axis.horizontal, 19 | this.bannerMargin = const EdgeInsets.only(left: 10.0, right: 10.0), 20 | this.bannerBorderRadius = const BorderRadius.all(Radius.circular(10.0)), 21 | this.bannerDefaultBGColor = Colors.grey, 22 | this.bannerTextAlignment = Alignment.center, 23 | this.bannerTextPadding = 24 | const EdgeInsets.only(left: 15.0, right: 15.0, top: 5.0, bottom: 5.0), 25 | this.bannerTextColor = Colors.white, 26 | this.bannerTextBGColor = Colors.black54, 27 | this.indicatorPositioned = const Positioned( 28 | top: 10.0, 29 | ), 30 | this.indicatorScrollDirection = Axis.horizontal, 31 | this.indicatorNormalColor: Colors.white, 32 | this.indicatorSelectedColor: Colors.white, 33 | this.indicatorNormalSize: 8.0, 34 | this.indicatorScaleSize: 1.4, 35 | this.indicatorSpacing: 4.0, 36 | this.indicatorStyle: PageViewIndicatorStyle.circle, 37 | this.indicatorAnimStyle: PageViewIndicatorAnimStyle.scaled, 38 | }); 39 | 40 | /// 内容数据 41 | final List data; 42 | 43 | /// 自定义内容显示样式 44 | final List customViewPageItemWidget; 45 | 46 | /// 自动滑动时间间隔[单位s] 47 | final int autoScrollDurationSeconds; 48 | 49 | /// Banner滑动方向[水平/垂直] 50 | final Axis bannerScrollDirection; 51 | 52 | /// Banner之间间距 53 | final EdgeInsetsGeometry bannerMargin; 54 | 55 | /// Banner圆角 56 | final BorderRadius bannerBorderRadius; 57 | 58 | /// Banner默认背景颜色 59 | final Color bannerDefaultBGColor; 60 | 61 | /// Banner文字位置 62 | final AlignmentGeometry bannerTextAlignment; 63 | 64 | /// Banner文字Padding 65 | final EdgeInsetsGeometry bannerTextPadding; 66 | 67 | /// Banner文字颜色 68 | final Color bannerTextColor; 69 | 70 | /// Banner文字背景颜色 71 | final Color bannerTextBGColor; 72 | 73 | /// 点击的事件监听 74 | final ValueChanged onPageTap; 75 | 76 | /// View高度 77 | final double height; 78 | 79 | /// 指示器位置 80 | final Positioned indicatorPositioned; 81 | 82 | /// 指示器方向 [水平/垂直] 83 | final Axis indicatorScrollDirection; 84 | 85 | /// 指示器默认颜色 86 | final Color indicatorNormalColor; 87 | 88 | /// 指示器选中颜色 89 | final Color indicatorSelectedColor; 90 | 91 | /// 指示器点默认大小 92 | final double indicatorNormalSize; 93 | 94 | /// 指示器点选中放大倍数,默认1.4倍 95 | final double indicatorScaleSize; 96 | 97 | /// 指示器点的间距 98 | final double indicatorSpacing; 99 | 100 | final PageViewIndicatorStyle indicatorStyle; 101 | 102 | /// 指示器动画样式[normal: 选中变色, scaled:选中放大] 103 | final PageViewIndicatorAnimStyle indicatorAnimStyle; 104 | 105 | /// 轮播控制器 106 | Timer _timer; 107 | 108 | /// 控制器 109 | final PageController _controller = PageController(initialPage: 200); 110 | 111 | /// 获得真实数据的长度 112 | int _getReallyDataSize() { 113 | return data != null ? data.length : 0; 114 | } 115 | 116 | /// 有触摸的时候,将轮播控制器状态清空 117 | _resetTimer() { 118 | if (_timer != null) { 119 | _timer.cancel(); 120 | } 121 | 122 | _timer = new Timer.periodic( 123 | new Duration(seconds: autoScrollDurationSeconds), (Timer timer) { 124 | if (_controller.page != null) { 125 | var nextPageIndex = _controller.page.toInt() + 1; 126 | _toPage(nextPageIndex); 127 | } 128 | }); 129 | } 130 | 131 | /// 跳转到指定ViewPager 132 | void _toPage(int page) { 133 | _controller.animateToPage(page, 134 | duration: Duration(milliseconds: 300), curve: Curves.ease); 135 | } 136 | 137 | /// 生成默认的Item样式 138 | Widget _generateViewPageDefaultItem(BuildContext context, int index) { 139 | return Container( 140 | margin: bannerMargin, 141 | child: Material( 142 | child: InkWell( 143 | onTap: () => onPageTap(index), 144 | onTapDown: _resetTimer(), 145 | child: ClipRRect( 146 | borderRadius: bannerBorderRadius, 147 | child: customViewPageItemWidget != null 148 | ? customViewPageItemWidget[index] 149 | : _generateDefaultView(index), 150 | ), 151 | ), 152 | )); 153 | } 154 | 155 | Widget _generateDefaultView(int index) { 156 | return Stack( 157 | children: [ 158 | Container( 159 | height: double.infinity, 160 | width: double.infinity, 161 | alignment: Alignment.center, 162 | color: bannerDefaultBGColor, 163 | child: CachedNetworkImage( 164 | height: double.infinity, 165 | width: double.infinity, 166 | alignment: Alignment.center, 167 | fit: BoxFit.cover, 168 | placeholder: CircularProgressIndicator(), 169 | imageUrl: data[index].photoUrl, 170 | ), 171 | ), 172 | _generateTextView(data[index].description), 173 | ], 174 | ); 175 | } 176 | 177 | /// 动态生成文字区域 178 | Widget _generateTextView(String content) { 179 | if (content == null || content.isEmpty) { 180 | return Container(); 181 | } 182 | 183 | return Positioned( 184 | left: 0.0, 185 | right: 0.0, 186 | bottom: 0.0, 187 | child: Container( 188 | color: bannerTextBGColor, 189 | padding: bannerTextPadding, 190 | alignment: bannerTextAlignment, 191 | child: Text( 192 | content, 193 | overflow: TextOverflow.ellipsis, 194 | maxLines: 2, 195 | style: TextStyle(color: bannerTextColor), 196 | )), 197 | ); 198 | } 199 | 200 | @override 201 | Widget build(BuildContext context) { 202 | _resetTimer(); 203 | 204 | return SizedBox( 205 | height: height, 206 | child: Container( 207 | child: Stack( 208 | alignment: Alignment.bottomCenter, 209 | children: [ 210 | PageView.builder( 211 | scrollDirection: bannerScrollDirection, 212 | controller: _controller, 213 | itemBuilder: (context, index) { 214 | return _generateViewPageDefaultItem( 215 | context, index % _getReallyDataSize()); 216 | }), 217 | Positioned( 218 | left: indicatorPositioned.left, 219 | top: indicatorPositioned.top, 220 | right: indicatorPositioned.right, 221 | bottom: indicatorPositioned.bottom, 222 | child: PageViewIndicator( 223 | controller: _controller, 224 | itemCount: _getReallyDataSize(), 225 | size: indicatorNormalSize, 226 | scrollDirection: indicatorScrollDirection, 227 | normalColor: indicatorNormalColor, 228 | selectedColor: indicatorSelectedColor, 229 | spacing: indicatorSpacing, 230 | scaleSize: indicatorScaleSize, 231 | animStyle: indicatorAnimStyle, 232 | style: indicatorStyle, 233 | onPageSelected: (int page) { 234 | _toPage(page); 235 | }, 236 | ), 237 | ), 238 | ], 239 | ))); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /banner_gallery_library/.idea/libraries/Dart_Packages.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | -------------------------------------------------------------------------------- /banner_gallery_library/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 36 | 37 | 38 | 39 | environment 40 | 41 | 42 | 43 | 46 | 47 | 48 | 55 | 56 | 57 | 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 |