在新闻类的App应用场景中, 基本上都会使用Banner展示。
效果截图
Banner 开发主要使用 Flutter的PageView组件;此组件类似于Android的ViewPager。实现原理也类似于Android中使用ViewPager开发Banner。
需要知道知识点就是:
1.Expanded 组件使用flex属性可以实现LinearLayout的layout_weight功能
2.文本组件(Text)不能设置大小。控制大小只能设置父容器的大小。
- IntrinsicWidth 组件有类似于 Android 宽度 wrap_content 功能(自适应子控件宽度); IntrinsicHeight 主键类似于 高度 wrap_content 功能(自适应子控件高度).
代码:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:transparent_image/transparent_image.dart';
const MAX_COUNT = 0x7fffffff;
///
/// Item的点击事件
///
typedef void OnBannerItemClick(int position, BannerItem entity);
///
/// 自定义ViewPager的每个页面显示
///
typedef Widget CustomBuild(int position, BannerItem entity);
class BannerWidget extends StatefulWidget {
final double height;
final List datas;
int duration;
double pointRadius;
Color selectedColor;
Color unSelectedColor;
Color textBackgroundColor;
bool isHorizontal;
OnBannerItemClick bannerPress;
CustomBuild build;
BannerWidget(double this.height, List this.datas,
{Key key,
int this.duration = 5000,
double this.pointRadius = 3.0,
Color this.selectedColor = Colors.red,
Color this.unSelectedColor = Colors.white,
Color this.textBackgroundColor = const Color(0x99000000),
bool this.isHorizontal = true,
OnBannerItemClick this.bannerPress,
CustomBuild this.build})
: super(key: key);
@override
BannerState createState() {
return new BannerState();
}
}
class BannerState extends State {
Timer timer;
int selectedIndex = 0;
PageController controller;
@override
void initState() {
double current = widget.datas.length > 0
? (MAX_COUNT / 2) - ((MAX_COUNT / 2) % widget.datas.length)
: 0.0;
controller = PageController(initialPage: current.toInt());
_initPageAutoScroll();
super.initState();
}
_initPageAutoScroll() {
start();
}
@override
void didUpdateWidget(BannerWidget oldWidget) {
super.didUpdateWidget(oldWidget);
print('didUpdateWidget----------------------banner');
}
start() {
stop();
timer = Timer.periodic(Duration(milliseconds: widget.duration), (timer) {
if(widget.datas.length > 0 && controller != null && controller.page != null) {
controller.animateToPage(controller.page.toInt() + 1,
duration: Duration(milliseconds: 300), curve: Curves.linear);
}
});
}
stop() {
timer?.cancel();
timer = null;
}
@override
void dispose() {
stop();
super.dispose();
}
@override
Widget build(BuildContext context) {
return new Container(
height: widget.height,
color: Colors.black12,
child: Stack(
children: [
getViewPager(),
new Align(
alignment: Alignment.bottomCenter,
child: IntrinsicHeight(
child: Container(
padding: EdgeInsets.all(6.0),
color: widget.textBackgroundColor,
child: getBannerTextInfoWidget(),
),
),
),
],
),
);
}
Widget getViewPager() {
return PageView.builder(
itemCount: widget.datas.length > 0 ? MAX_COUNT : 0,
controller: controller,
onPageChanged: onPageChanged,
itemBuilder: (context, index) {
return InkWell(
onTap: () {
if (widget.bannerPress != null)
widget.bannerPress(selectedIndex, widget.datas[selectedIndex]);
},
child: widget.build == null
? FadeInImage.memoryNetwork(
placeholder: kTransparentImage,
image:
widget.datas[index % widget.datas.length].itemImagePath,
fit: BoxFit.cover)
: widget.build(
index, widget.datas[index % widget.datas.length]));
},
);
}
Widget getSelectedIndexTextWidget() {
return widget.datas.length > 0 && selectedIndex < widget.datas.length
? widget.datas[selectedIndex].itemText
: Text('');
}
Widget getBannerTextInfoWidget() {
if (widget.isHorizontal) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
new Expanded(
flex: 1,
child: getSelectedIndexTextWidget(),
),
Expanded(
flex: 0,
child: Row(
mainAxisSize: MainAxisSize.min,
children: circle(),
),
),
],
);
} else {
return new Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
getSelectedIndexTextWidget(),
IntrinsicWidth(
child: Row(
children: circle(),
),
),
],
);
}
}
List circle() {
List circle = [];
for (var i = 0; i < widget.datas.length; i++) {
circle.add(Container(
margin: EdgeInsets.all(2.0),
width: widget.pointRadius * 2,
height: widget.pointRadius * 2,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: selectedIndex == i
? widget.selectedColor
: widget.unSelectedColor,
),
));
}
return circle;
}
onPageChanged(index) {
selectedIndex = index % widget.datas.length;
setState(() {});
}
}
class BannerItem {
String itemImagePath;
Widget itemText;
static BannerItem defaultBannerItem(String image, String text) {
BannerItem item = new BannerItem();
item.itemImagePath = image;
Text textWidget = new Text(
text,
softWrap: true,
maxLines: 3,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white, fontSize: 12.0, decoration: TextDecoration.none),
);
item.itemText = textWidget;
return item;
}
}
注意:
1.此工程需依赖库 transparent_image: ^0.1.0
用于加载图片
2.注意集合大小为0的情况。
itemCount: widget.datas.length > 0 ? MAX_COUNT : 0, //widget.datas集合变化时更新数据
使用示例:
List bannerList = [];
@override
void initState() {
BannerItem item = BannerItem.defaultBannerItem(
'''http://n.sinaimg.cn/news/1_img/vcg/2b0c102b/64/w1024h640/20181024/wBkr-hmuuiyw6863395.jpg''',
'''近日,北大全校教师干部大会刚刚召开,63岁的林建华卸任北大校长;原北大党委书记郝平接替林建华,成为新校长。曾在北京任职多年、去年担任山西高院院长的邱水平回到北大,担任党委书记。图为2018年5月4日,北京大学举行建校120周年纪念大会,时任北京大学校长林建华(右)与时任北京大学党委书记郝平(左)''');
bannerList.add(item);
item = BannerItem.defaultBannerItem(
'''http://n.sinaimg.cn/news/1_img/vcg/2b0c102b/99/w1024h675/20181024/FGXD-hmuuiyw6863401.jpg''',
'''邱水平、郝平、林建华均为“老北大人”,都曾离开北大,又重归北大任职。图为2018年5月4日,北京大学举行建校120周年纪念大会,时任北京大学党委书记郝平讲话''');
bannerList.add(item);
item = BannerItem.defaultBannerItem(
'''http://n.sinaimg.cn/news/1_img/vcg/2b0c102b/107/w1024h683/20181024/kZj2-hmuuiyw6863420.jpg''',
'''此番卸任的林建华,亦是北大出身,历任重庆大学、浙江大学、北京大学三所“双一流”高校校长。图为2018年5月4日,北京大学举行建校120周年纪念大会,时任北京大学校长林建华讲话。''');
bannerList.add(item);
item = BannerItem.defaultBannerItem(
'''http://n.sinaimg.cn/news/1_img/vcg/2b0c102b/105/w1024h681/20181024/tOiL-hmuuiyw6863462.jpg''',
'''书记转任校长的郝平,为十九届中央委员会候补委员。从北大毕业后留校,后离开北大,历任北京外国语大学校长、教育部副部长。2016年12月,时隔11年,郝平再回北大,出任北大党委书记。''');
bannerList.add(item);
super.initState();
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: Text('news list and banner'),
),
body: Center(
child: Column(
children: [
new BannerWidget(
180.0,
bannerList,
bannerPress: (pos, item) {
print('第 $pos 点击了');
},
),
],
),
),
);
}