Wrap
以下是flutter默认给我们提供的接口
Wrap({
Key key,
this.direction = Axis.horizontal,
this.alignment = WrapAlignment.start,
this.spacing = 0.0,
this.runAlignment = WrapAlignment.start,
this.runSpacing = 0.0,
this.crossAxisAlignment = WrapCrossAlignment.start,
this.textDirection,
this.verticalDirection = VerticalDirection.down,
List<Widget> children = const <Widget>[],
}) : super(key: key, children: children);
提供了很多属性,介绍一下属性都是干嘛的
属性 | 作用 |
---|---|
direction | 扩展方式 比如横向堆砌 |
alignment | 对齐方式 |
spacing | 主轴空隙间距 |
runAlignment run | 的对齐方式 |
runSpacing run | 空隙间距 |
crossAxisAlignment | 纵轴对齐方式 |
textDirection | 文本对齐方向 |
verticalDirection | 确定垂直放置子元素的顺序,以及如何在垂直方向上解释开始和结束,默认down children 需要放置的组件列表 |
当选中该金额项目时,触发点击事件和样式,如下用Wrap实现,for循环遍历,拿到当前索引值
int specIndex;
Wrap(
runSpacing: 10,
spacing: 20,
children: _buildMoneyItem(),
),
List<Widget> _buildMoneyItem() {
List<Widget> spectListWidget = [];
for (int i = 0; i < list.length; i++) {
spectListWidget.add(GestureDetector(
onTap: () {
selectedAccountValueSave = listValue[i];
setState(() {
specIndex = i;
});
},
child: Container(
height: 40,
width: 75,
alignment: Alignment.center,
child: Center(
child: Text(
list[i],
style: TextStyle(fontSize: 16),
),
),
// padding: EdgeInsets.only(left: 10, right: 10),
decoration: BoxDecoration(
border: Border(
top: BorderSide(
width: 1.0, color: specIndex == i ? colorRed : colorGrey),
left: BorderSide(
width: 1.0, color: specIndex == i ? colorRed : colorGrey),
right: BorderSide(
width: 1.0, color: specIndex == i ? colorRed : colorGrey),
bottom: BorderSide(
width: 1.0, color: specIndex == i ? colorRed : colorGrey),
),
color: Color(0xFFf7f7f7),
borderRadius: BorderRadius.all(Radius.circular(2))),
),
));
}
return spectListWidget;
}
控件网格的主轴方向是它滚动的方向( scrollDirection)。 最常用的网格布局是
GridView.count
,它创建了一个在横轴上具有固定数量 网格块 的平铺的布局
GridView源码
GridView({
Key key,
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
bool shrinkWrap = false,
EdgeInsetsGeometry padding,
@required this.gridDelegate,
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
bool addSemanticIndexes = true,
double cacheExtent,
List<Widget> children = const <Widget>[],
int semanticChildCount,
})
属性 | 值类型 | 说明 |
---|---|---|
scrollDirection | Axis | 设置滚动的方向,horizontal(水平)或vertical(垂直) |
reverse bool | 是否翻转 | |
controller | ScrollController | 用来控制滚动位置及监听滚动事件 |
shrinkWrap | bool | 是否根据子widget的总长度来设置GridView的长度 |
padding | EdgeInsetsGeometry | 间距 |
gridDelegate | SliverGridDelegate | 控制子Widget如何进行布局 |
children | List | 子控件 |
gridDelegate
该属性接收一个SliverGridDelegate类型的值,主要是用来控制子Widget如何进行布局。
他有如下两个实现类
SliverGridDelegateWithMaxCrossAxisExtent和SliverGridDelegateWithFixedCrossAxisCount
构造方法
const SliverGridDelegateWithMaxCrossAxisExtent({
@required this.maxCrossAxisExtent, //子控件的最大宽度,实际宽度是根据交叉轴的值进行平分,也就是说最大宽度并不一定是实际宽度,很有可能子控件的实际宽度要小于设置的最大宽度
this.mainAxisSpacing = 0.0, //主轴之间的间距
this.crossAxisSpacing = 0.0,//交叉轴之间的间距
this.childAspectRatio = 1.0,//子控件的宽高比
}
GridView(
scrollDirection: Axis.vertical,
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 100, //子控件最大宽度为100
childAspectRatio: 0.5,//宽高比为1:2
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
padding: EdgeInsets.all(10),
children: List.generate(
20,
(index) {
return Box(index + 1);
},
),
);
List.generate()
List.generate(images.length, (i){
return Container(
margin: EdgeInsets.only(left:20.0),
child:
Image.network("${images[i]}",
width: 375.0,
fit: BoxFit.fitWidth,
),
);
}),
实战举例
GridView.count(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
crossAxisSpacing: 20,
crossAxisCount: 3,
children: menuList.map((e) {
return InkWell(
onTap: () {
jumpToTargetPage(e.tag);
},
child: Column(
children: [
Image.asset(
e.icon,
height: 36,
width: 36,
),
SizedBox(height: 10),
Text(
e.title,
style: TextStyle(fontSize: 12, color: Colours.textBlack32),
),
],
),
);
}).toList(),
)
void jumpToTargetPage(String tag) {
String pageName;
Map arguments;
switch (tag) {
case "1":
pushNamedPage(PublishRoute, arg: arguments);
break;
case "2":
break;
}
if (pageName != null) {
pushNamedPage(pageName, arg: arguments);
}
}
ListView.builder 是一种构建列表的方法,其中的子 Widget 可以按需构建。但是,与返回静态 Widget 不同的是,它会多次调用(基于 itemCount)一个生成函数,并可在每次调用时返回不同的 Widget。
无限循环列表
ListView.builder(
itemBuilder: (context, index) {
return ListTile(
leading: Icon(Icons.shopping_cart),
title: Text('product $index'),
subtitle: Text('price: ${Random().nextInt(100)} USD'),
);
}
)
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (ctx, index) {
NewsModel news = accountViewModel.newsList[index];
return InkWell(
onTap: () {
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Row(
children: [
Image.asset(
"resource/images/temp_news.png",
width: 110,
height: 70,
fit: BoxFit.fill,
),
SizedBox(width: 10),
Expanded(
child: Container(
height: 70,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
news.title,
style: TextStyle(
fontSize: 14, color: Colours.textBlack32),
overflow: TextOverflow.ellipsis,
softWrap: true,
maxLines: 2,
),
Text(
news.pubDate,
style: TextStyle(
fontSize: 12, color: Color(0xFFA3A0A0)),
),
],
),
),
)
],
),
),
);
},
itemCount: accountViewModel.newsList.length,
)
解决Flutter ListView 或者SingleChildScrollView 嵌套 ListView.builder滑动冲突
原因
SingleChildScrollView 和 ListView 都有滚动属性physics 他们默认是都是可以滚动的,
ListView 嵌套 ListView.builder 需要后者shrinkWrap = true,不然报错;
解决方式
禁用 ListView 的滚动physics 保留 SingleChildScrollView 的滚动
Listview 执行 physics 属性 new NeverScrollableScrollPhysics(), //禁用
new ListView.builder(
shrinkWrap: true,
physics: new NeverScrollableScrollPhysics(),
)