参考
一般来说,在 Flutter 中写可选择列表的思路就是将列表中的选择状态用一个数组保存起来,每当用户点击列表中的任意一项的时候,这个数组便会更新。
class SomeListViewState extend State<SomeListView> {
//列表状态
final selections = <bool>[];
@override
void initState() {
super.initState();
//状态全部设置为false
selections.addAll(List.filled(widget.data.length, false));
}
@override
Widget build() {
return ListView(
...
children: widget.data.mapWithIndex((detail, index) {
return SomeWidget(
...
selected: selection[index],
onTap:() {
//当点击的时候调用函数,传index过去
_tapItem(detail, index);
},
);
},
);
}
_tapItem(SomeData detail, int index) {
setState(() {
//设置该item所在的列表中的状态
selections[index] = !selections[index];
});
}
}
我自己的代码
import 'dart:convert';
import 'dart:developer';
import 'dart:ui';
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:music/data/AuthCode.dart';
import 'package:music/data/DynamicDetailsData.dart';
import 'package:music/data/DynamicMessageData.dart';
import 'package:music/data/GetFocusListData.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'DynamicDetails.dart';
import 'Utils/AsperctRaioImage.dart';
import 'Utils/StringUtil.dart';
/*
* 自己的动态列表
*/
class MySelfDynamic extends StatefulWidget {
MySelfDynamic({key}) : super(key: key);
@override
//状态
MySelfDynamicState createState() => MySelfDynamicState();
}
class MySelfDynamicState extends State<MySelfDynamic> {
//将列表中的选择状态用一个数组保存起来
final selections = <bool>[];
//数据
var _items = [];
//判断管理是否被点击
bool deleteState = false;
//这个是单击选项保存ID
var deleteMomentId = [];
//这个是通过全选保存ID
var allDeleteMoment = [];
@override
void initState() {
// TODO: implement initState
getMyselfDynamicList();
//初始化先让item状态列表全部为false,点击其中一项,就让那项为true
selections.addAll(List.filled(_items.length, false));
super.initState();
}
@override
void dispose() {
//销毁
super.dispose();
}
//获取自己的动态接口
getMyselfDynamicList() async {
var apiUrl = "http://47.242.63.216:9527/v1/moment";
SharedPreferences prefs = await SharedPreferences.getInstance();
var tokens = prefs.getString("token");
var userId = prefs.getInt("userId");
//参数
Map map = {};
map["page"] = 1;
map["page_size"] = 2;
map["user_id"] = userId;
//网络请求添加token请求头
Response result = await Dio().post(apiUrl,
data: map, options: Options(headers: {"x-token": tokens}));
log("${result}");
//json解析
Map<String, dynamic> nickname = json.decode(result.toString());
var httpRes = DynamicMessageData.fromJson(nickname);
if (httpRes.code == 200) {
setState(() {
_items = httpRes.data.list;
//重新获取数据的时候清理item状态,且重新赋值为false
selections.clear();
selections.addAll(List.filled(_items.length, false));
});
}
}
//删除自己的动态接口
deleteMyselfDynamicList(momentId) async {
var apiUrl = "http://47.242.63.216:9527/v1/moment";
SharedPreferences prefs = await SharedPreferences.getInstance();
var tokens = prefs.getString("token");
//参数
Map map = {};
map["moment_id"] = momentId;
//网络请求添加token请求头
Response result = await Dio().post(apiUrl,
data: map, options: Options(headers: {"x-token": tokens}));
log("${result}");
//json解析
Map<String, dynamic> nickname = json.decode(result.toString());
var httpRes = AuthCode.fromJson(nickname);
if (httpRes.code == 200) {
setState(() {
//如果删除成功就退出删除界面
deleteState = false;
//同时重新获取一遍数据
getMyselfDynamicList();
});
}
}
//单选item,保存momentId
_tapItem(momentId, index) {
setState(() {
//改变列表item的状态
selections[index] = !selections[index];
});
//判断,如果状态是真的,就赋值给deleteMomentId
if (selections[index]) {
deleteMomentId.add(momentId);
} else {
//反之移除
deleteMomentId.remove(momentId);
}
}
//点击全选的时候情况
getAllMomentId() {
//先清除状态列表
selections.clear();
setState(() {
//重新设置为true,也就是全部都已选择
selections.addAll(List.filled(_items.length, true));
});
}
//item方法体
Widget itemView(BuildContext context, int index) {
MessageList model = this._items[index];
//这里对全选的情况作判断,如果该item的状态为真,而且还没有添加到allDeleteMoment,就把该item的momentId添加进去
if (selections[index]) if (!allDeleteMoment
.contains(model.momentInfo.momentId))
allDeleteMoment.add(model.momentInfo.momentId);
//反之移除
if (!selections[index]) allDeleteMoment.remove(model.momentInfo.momentId);
return GestureDetector(
onTap: () {},
child: Column(
children: [
Container(
//用decoration的BoxDecoration设置圆角
decoration: BoxDecoration(),
//左图中的第二个红框部分
//边距
margin: const EdgeInsets.only(left: 15.0, top: 15),
//内边距
padding: const EdgeInsets.only(bottom: 10.0), //这个是该容器进行设置边距
//在这个容器里面放着一行控件
child: Row(
//行里的控件
children: [
if (deleteState)
GestureDetector(
onTap: () {
_tapItem(model.momentInfo.momentId, index);
},
child: Container(
height: 20,
width: 20,
margin: EdgeInsets.only(right: 15),
decoration: selections[index]
? BoxDecoration(
image: DecorationImage(
image: AssetImage(
"assets/base_widgets/icon_publish_checked.png"),
fit: BoxFit.fitWidth,
))
: BoxDecoration(
image: DecorationImage(
image: AssetImage(
"assets/base_widgets/icon_publish_moment_unchecked.png"),
fit: BoxFit.fitWidth,
),
),
),
),
Row(
children: [
//月
Container(
margin: EdgeInsets.only(right: 5),
child: Text(
"${StringUtils.getCreateDynamicYearByTimeStamp(model.momentInfo.createTime)}",
style:
TextStyle(color: Color(0xFFBBBBBB), fontSize: 14),
),
),
//日
Container(
margin: EdgeInsets.only(right: 5),
child: Text(
"${StringUtils.getCreateDynamicMonthByTimeStamp(model.momentInfo.createTime)}",
style:
TextStyle(color: Color(0xFFBBBBBB), fontSize: 18),
),
),
],
),
//如果有图片就显示图片
if (model.momentInfo.photos.length > 0)
if (model.momentInfo.video == "")
GestureDetector(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return DynamicDetails(
//手机号
moment_id: model.momentInfo.momentId,
user_id: model.userInfo.userId,
);
}));
},
child: Container(
child: AsperctRaioImage.network(
model.momentInfo.photos[0],
builder: (context, snapshot, url) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(
left: 16, right: 16, bottom: 5),
width: snapshot.data.width.toDouble() / 15,
height: snapshot.data.height.toDouble() / 15,
child: FadeInImage.assetNetwork(
placeholder:
"assets/base_widgets/icon_publish_moment_addphoto.png",
image: url,
imageErrorBuilder:
(context, error, stackTrace) {
return Container(
width: 100,
height: 100,
color: Colors.grey,
);
},
fit: BoxFit.cover,
),
)
],
);
})),
),
if (model.momentInfo.photos.length > 0)
//一张图片,如果视频不为空
if (model.momentInfo.video != "")
GestureDetector(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return DynamicDetails(
//手机号
moment_id: model.momentInfo.momentId,
user_id: model.userInfo.userId,
);
}));
},
child: Container(
decoration: BoxDecoration(),
child: AsperctRaioImage.network(
model.momentInfo.photos[0],
builder: (context, snapshot, url) {
return Stack(
alignment: Alignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.only(
left: 16, right: 16, bottom: 5),
width: snapshot.data.width.toDouble() / 1,
height: snapshot.data.height.toDouble() / 1,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(url),
fit: BoxFit.contain,
),
),
),
Container(
width: 50,
margin: const EdgeInsets.only(
left: 15, right: 15),
child: Image.asset(
'assets/base_widgets/icon_player_play.png',
fit: BoxFit.fitWidth,
),
),
],
);
})),
),
//行的第二个元素,Expanded相当于设置宽度为0,倍数为1,后面的控件就去到最后面了
Expanded(
//Container和Expanded都是只是描述的限制,他们后面的元素都要加child:
//该部分里面是用列排布的
child: Column(
//设置子项左对齐
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//动态的文字
Container(
margin: EdgeInsets.only(left: 15),
child: Text(
'${model.momentInfo.text}',
style: TextStyle(
color: Color(0xFFBBBBBB), fontSize: 14.0),
),
),
//有图片的时候显示图标位置信息
if (model.momentInfo.photos.length > 0)
Container(
margin: EdgeInsets.only(top: 10),
child: Row(
children: [
Container(
width: 9,
margin: EdgeInsets.only(right: 5),
child: Image.asset(
'assets/base_widgets/icon_personal_area.png',
fit: BoxFit.fitWidth,
),
),
Container(
margin: EdgeInsets.only(),
child: Text(
'${model.userInfo.area}',
style: TextStyle(
color: Color(0xFF666666), fontSize: 14.0),
),
),
],
),
),
],
),
),
],
),
),
//分割线
Container(
margin: const EdgeInsets.only(bottom: 10, left: 15, right: 15),
height: 1,
decoration: const BoxDecoration(color: Color(0xFF191919)),
),
],
),
);
}
//最上面的年份
Widget yearSection = Container(
margin: EdgeInsets.only(left: 15, top: 15),
child: Row(
children: [
Text(
"2022年",
style: TextStyle(color: Color(0xFFBBBBBB), fontSize: 14),
),
],
),
);
Widget deleteDialog() {
return Container(
decoration: BoxDecoration(color: Color(0xFF333333)),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
title: const Center(
child: Text(
"確認",
style: TextStyle(color: Color(0xFFDDDDDD)),
),
),
onTap: () {
//如果单击选项时产生的列表不为空,就循环获取ID请求接口
if (deleteMomentId.length > 0)
for (int i = 0; i < deleteMomentId.length; i++) {
deleteMyselfDynamicList(deleteMomentId[i]);
}
//如果点击全选产生的列表不为空,就循环获取ID请求接口
if(allDeleteMoment.length>0)
for(int i=0;i<allDeleteMoment.length;i++){
deleteMyselfDynamicList(allDeleteMoment[i]);
}
},
),
Container(
decoration: BoxDecoration(color: Color(0xFF444444)),
height: 1,
),
ListTile(
title: const Center(
child: Text(
"取消",
style: TextStyle(color: Color(0xFFDDDDDD)),
),
),
onTap: () {},
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Material(
child: Scaffold(
appBar: AppBar(
//leading设置状态栏左边的图标
leading: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (deleteState)
GestureDetector(
onTap: () {
getAllMomentId();
},
child: Container(
margin: const EdgeInsets.only(left: 5.0),
width: 40,
child: Text(
"全选",
style: TextStyle(color: Color(0xFF999999), fontSize: 14),
)),
),
if (!deleteState)
GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Container(
margin: const EdgeInsets.only(left: 5.0),
width: 40,
child: Image.asset(
'assets/base_widgets/icon_register_back.png',
fit: BoxFit.fitWidth,
),
),
),
],
),
actions: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () {
setState(() {
deleteState = !deleteState;
});
},
child: Container(
margin: EdgeInsets.only(right: 5),
child: deleteState
? Image.asset(
"assets/base_widgets/icon_login_account_close.png",
width: 50,
height: 24,
fit: BoxFit.fitWidth,
)
: Text(
"管理",
style:
TextStyle(color: Color(0xFF999999), fontSize: 14),
),
),
)
],
),
],
title: deleteState ? Text('批量刪除') : Text('我的動態'),
backgroundColor: Color(0xFF222222),
//文字居中
centerTitle: true,
),
body: Container(
//设置界面背景
decoration: BoxDecoration(
color: Color(0xFF222222),
),
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
yearSection,
ListView.builder(
itemCount: _items.length,
itemBuilder: itemView,
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
),
],
))),
if (deleteState) deleteDialog()
],
),
),
));
}
}
除此之外还想到另外一个方法,这个用于item数量少的情况
//先记录ID为1
int? priceId=1;
Container(
width: double.infinity,
child: ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: buyTimesPrice?.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: (){
setState((){
//点击事件,赋值ID即可
priceId=buyTimesPrice![index].id;
});
},
child: Container(
margin: const EdgeInsets.only(
left: 20, right: 20, bottom: 10),
padding: const EdgeInsets.only(
top: 5, bottom: 5, left: 10, right: 10),
//判断当前ID是否等于全局的那个ID,是就显示不同颜色
decoration: priceId==buyTimesPrice![index].id?BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(50)):BoxDecoration(
color: Colors.yellow,
borderRadius: BorderRadius.circular(50)),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Container(
child: Text(
buyTimesPrice![index].times.toString() +
"次",
style: const TextStyle(color: Colors.white),
),
),
),
Container(
margin: const EdgeInsets.only(),
child: const Image(
image: AssetImage(
'assets/images/icon_game_shake_love@2x.png'),
gaplessPlayback: true,
width: 18,
height: 17),
),
Container(
margin: const EdgeInsets.only(left: 5),
child: Text(
buyTimesPrice![index].price.toString(),
style: const TextStyle(color: Colors.white),
),
),
],
),
),
);
},
),
),