feat: 私信查看

This commit is contained in:
guozhigq
2023-10-15 16:12:09 +08:00
parent 94aef39f7b
commit b272d25157
13 changed files with 1048 additions and 1 deletions

View File

@ -129,7 +129,11 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
child: Row(
children: [
const Expanded(child: SearchPage()),
const SizedBox(width: 10),
const SizedBox(width: 6),
IconButton(
onPressed: () => Get.toNamed('/whisper'),
icon: const Icon(Icons.notifications_none)),
const SizedBox(width: 6),
Obx(
() => ctr!.userLogin.value
? Stack(

View File

@ -0,0 +1,50 @@
import 'package:get/get.dart';
import 'package:pilipala/http/msg.dart';
import 'package:pilipala/models/msg/account.dart';
import 'package:pilipala/models/msg/session.dart';
class WhisperController extends GetxController {
RxList<SessionList> sessionList = <SessionList>[].obs;
RxList<AccountListModel> accountList = <AccountListModel>[].obs;
Future querySessionList() async {
var res = await MsgHttp.sessionList();
if (res['data'].sessionList.isNotEmpty) {
await queryAccountList(res['data'].sessionList);
// 将 accountList 转换为 Map 结构
Map<int, dynamic> accountMap = {};
for (var j in accountList) {
accountMap[j.mid!] = j;
}
// 遍历 sessionList通过 mid 查找并赋值 accountInfo
for (var i in res['data'].sessionList) {
var accountInfo = accountMap[i.talkerId];
if (accountInfo != null) {
i.accountInfo = accountInfo;
}
if (i.talkerId == 844424930131966) {
i.accountInfo = AccountListModel(
name: 'UP主小助手',
face:
'https://message.biliimg.com/bfs/im/489a63efadfb202366c2f88853d2217b5ddc7a13.png',
);
}
}
}
if (res['status']) {
sessionList.value = res['data'].sessionList;
}
return res;
}
Future queryAccountList(sessionList) async {
List midsList = sessionList.map((e) => e.talkerId!).toList();
var res = await MsgHttp.accountList(midsList.join(','));
if (res['status']) {
accountList.value = res['data'];
}
return res;
}
}

View File

@ -0,0 +1,4 @@
library whisper;
export './controller.dart';
export './view.dart';

178
lib/pages/whisper/view.dart Normal file
View File

@ -0,0 +1,178 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/utils/utils.dart';
import 'controller.dart';
class WhisperPage extends StatefulWidget {
const WhisperPage({super.key});
@override
State<WhisperPage> createState() => _WhisperPageState();
}
class _WhisperPageState extends State<WhisperPage> {
late final WhisperController _whisperController =
Get.put(WhisperController());
late Future _futureBuilderFuture;
@override
void initState() {
super.initState();
_futureBuilderFuture = _whisperController.querySessionList();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
scrolledUnderElevation: 0,
),
body: Column(
children: [
// LayoutBuilder(
// builder: (BuildContext context, BoxConstraints constraints) {
// // 在这里根据父级容器的约束条件构建小部件树
// return Padding(
// padding: const EdgeInsets.only(left: 20, right: 20),
// child: SizedBox(
// height: constraints.maxWidth / 5,
// child: GridView.count(
// primary: false,
// crossAxisCount: 4,
// padding: const EdgeInsets.all(0),
// childAspectRatio: 1.25,
// children: [
// Column(
// crossAxisAlignment: CrossAxisAlignment.center,
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// SizedBox(
// width: 36,
// height: 36,
// child: IconButton(
// style: ButtonStyle(
// padding:
// MaterialStateProperty.all(EdgeInsets.zero),
// backgroundColor:
// MaterialStateProperty.resolveWith((states) {
// return Theme.of(context)
// .colorScheme
// .primary
// .withOpacity(0.1);
// }),
// ),
// onPressed: () {},
// icon: Icon(
// Icons.message_outlined,
// size: 18,
// color: Theme.of(context).colorScheme.primary,
// ),
// ),
// ),
// const SizedBox(height: 6),
// const Text('回复我的', style: TextStyle(fontSize: 13))
// ],
// ),
// ],
// ),
// ),
// );
// },
// ),
Expanded(
child: FutureBuilder(
future: _futureBuilderFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Map data = snapshot.data as Map;
if (data['status']) {
List sessionList = _whisperController.sessionList;
return Obx(
() => sessionList.isEmpty
? const SizedBox()
: ListView.builder(
itemCount: sessionList.length,
shrinkWrap: true,
itemBuilder: (_, int i) {
return ListTile(
onTap: () {
Get.toNamed(
'/whisperDetail?talkerId=${sessionList[i].talkerId}&name=${sessionList[i].accountInfo.name}');
},
leading: NetworkImgLayer(
width: 45,
height: 45,
type: 'avatar',
src: sessionList[i].accountInfo.face,
),
title: Text(
sessionList[i].accountInfo.name,
style:
Theme.of(context).textTheme.titleSmall,
),
subtitle: Text(
sessionList[i].lastMsg.content['text'] ??
sessionList[i]
.lastMsg
.content['content'] ??
sessionList[i]
.lastMsg
.content['title'],
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.labelMedium!
.copyWith(
color: Theme.of(context)
.colorScheme
.outline)),
trailing: Text(
Utils.dateFormat(
sessionList[i].lastMsg.timestamp),
style: Theme.of(context)
.textTheme
.labelSmall!
.copyWith(
color: Theme.of(context)
.colorScheme
.outline),
),
);
},
),
);
} else {
// 请求错误
return SizedBox();
}
} else {
// 骨架屏
return SizedBox();
}
},
),
),
// ListTile(
// onTap: () {},
// leading: CircleAvatar(),
// title: Text('钱瑞昌'),
// subtitle: Text('没事', style: Theme.of(context).textTheme.bodySmall),
// trailing: Text('昨天'),
// ),
// ListTile(
// onTap: () {},
// leading: CircleAvatar(),
// title: Text('李天'),
// subtitle:
// Text('明天有空吗', style: Theme.of(context).textTheme.bodySmall),
// trailing: Text('现在'),
// )
],
),
);
}
}

View File

@ -0,0 +1,24 @@
import 'package:get/get.dart';
import 'package:pilipala/http/msg.dart';
import 'package:pilipala/models/msg/session.dart';
class WhisperDetailController extends GetxController {
late int talkerId;
RxString name = ''.obs;
RxList<MessageItem> messageList = <MessageItem>[].obs;
@override
void onInit() {
super.onInit();
talkerId = int.parse(Get.parameters['talkerId']!);
name.value = Get.parameters['name']!;
}
Future querySessionMsg() async {
var res = await MsgHttp.sessionMsg(talkerId: talkerId);
if (res['status']) {
messageList.value = res['data'].messages;
}
return res;
}
}

View File

@ -0,0 +1,4 @@
library whisper_detail;
export './controller.dart';
export './view.dart';

View File

@ -0,0 +1,167 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/pages/whisperDetail/controller.dart';
import 'widget/chat_item.dart';
class WhisperDetailPage extends StatefulWidget {
const WhisperDetailPage({super.key});
@override
State<WhisperDetailPage> createState() => _WhisperDetailPageState();
}
class _WhisperDetailPageState extends State<WhisperDetailPage> {
final WhisperDetailController _whisperDetailController =
Get.put(WhisperDetailController());
late Future _futureBuilderFuture;
@override
void initState() {
super.initState();
_futureBuilderFuture = _whisperDetailController.querySessionMsg();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
scrolledUnderElevation: 0,
title: SizedBox(
width: double.infinity,
height: 50,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: 34,
height: 34,
child: IconButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
backgroundColor:
MaterialStateProperty.resolveWith((states) {
return Theme.of(context)
.colorScheme
.primaryContainer
.withOpacity(0.6);
}),
),
onPressed: () => Get.back(),
icon: Icon(
Icons.arrow_back_outlined,
size: 18,
color: Theme.of(context).colorScheme.primary,
),
),
),
Obx(
() => Text(
_whisperDetailController.name.value,
style: Theme.of(context).textTheme.titleMedium,
),
),
const SizedBox(width: 36, height: 36),
],
),
),
),
body: FutureBuilder(
future: _futureBuilderFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Map data = snapshot.data as Map;
if (data['status']) {
List messageList = _whisperDetailController.messageList;
return Obx(
() => messageList.isEmpty
? const SizedBox()
: ListView.builder(
itemCount: messageList.length,
shrinkWrap: true,
reverse: true,
itemBuilder: (_, int i) {
if (i == 0) {
return Column(
children: [
ChatItem(item: messageList[i]),
const SizedBox(height: 12),
],
);
} else {
return ChatItem(item: messageList[i]);
}
},
),
);
} else {
// 请求错误
return const SizedBox();
}
} else {
// 骨架屏
return const SizedBox();
}
},
),
bottomNavigationBar: Container(
width: double.infinity,
height: MediaQuery.of(context).padding.bottom + 70,
padding: EdgeInsets.only(
left: 8,
right: 12,
bottom: MediaQuery.of(context).padding.bottom,
),
decoration: BoxDecoration(
color: Colors.white,
border: Border(
top: BorderSide(
width: 4,
color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// IconButton(
// onPressed: () {},
// icon: Icon(
// Icons.add_circle_outline,
// color: Theme.of(context).colorScheme.outline,
// ),
// ),
IconButton(
onPressed: () {},
icon: Icon(
Icons.emoji_emotions_outlined,
color: Theme.of(context).colorScheme.outline,
),
),
// Expanded(
// child: Container(
// height: 42,
// decoration: BoxDecoration(
// color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
// borderRadius: BorderRadius.circular(40.0),
// ),
// child: TextField(
// readOnly: true,
// style: Theme.of(context).textTheme.titleMedium,
// decoration: const InputDecoration(
// border: InputBorder.none, // 移除默认边框
// hintText: '请输入内容', // 提示文本
// contentPadding: EdgeInsets.symmetric(
// horizontal: 12.0, vertical: 12.0), // 内边距
// ),
// ),
// ),
// ),
const SizedBox(width: 16),
],
),
),
);
}
}

View File

@ -0,0 +1,189 @@
// ignore_for_file: must_be_immutable
import 'package:flutter/material.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/utils/utils.dart';
class ChatItem extends StatelessWidget {
dynamic item;
ChatItem({
super.key,
this.item,
});
@override
Widget build(BuildContext context) {
bool isOwner = item.senderUid == 17340771;
bool isPic = item.msgType == 2;
bool isText = item.msgType == 1;
bool isSystem =
item.msgType == 18 || item.msgType == 10 || item.msgType == 13;
int msgType = item.msgType;
Map content = item.content ?? '';
return isSystem
? (msgType == 10
? SystemNotice(item: item)
: msgType == 13
? SystemNotice2(item: item)
: const SizedBox())
: Row(
children: [
if (!isOwner) const SizedBox(width: 12),
if (isOwner) const Spacer(),
Container(
constraints: const BoxConstraints(
maxWidth: 300.0, // 设置最大宽度为200.0
),
decoration: BoxDecoration(
color: isOwner
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.secondaryContainer,
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(16),
topRight: const Radius.circular(16),
bottomLeft: Radius.circular(isOwner ? 16 : 6),
bottomRight: Radius.circular(isOwner ? 6 : 16),
),
),
margin: const EdgeInsets.only(top: 12),
padding: EdgeInsets.only(
top: 8,
bottom: 6,
left: isPic ? 8 : 12,
right: isPic ? 8 : 12,
),
child: Column(
crossAxisAlignment: isOwner
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
children: [
isText
? Text(
content['content'],
style: TextStyle(
color: isOwner
? Theme.of(context).colorScheme.onPrimary
: Theme.of(context)
.colorScheme
.onSecondaryContainer),
)
: isPic
? NetworkImgLayer(
width: 220,
height:
220 * content['height'] / content['width'],
src: content['url'],
)
: const SizedBox(),
SizedBox(height: isPic ? 7 : 2),
Text(
Utils.dateFormat(item.timestamp),
style: Theme.of(context).textTheme.labelSmall!.copyWith(
color: isOwner
? Theme.of(context)
.colorScheme
.onPrimary
.withOpacity(0.8)
: Theme.of(context)
.colorScheme
.onSecondaryContainer
.withOpacity(0.8)),
)
],
),
),
if (!isOwner) const Spacer(),
if (isOwner) const SizedBox(width: 12),
],
);
}
}
class SystemNotice extends StatelessWidget {
dynamic item;
SystemNotice({super.key, this.item});
@override
Widget build(BuildContext context) {
Map content = item.content ?? '';
return Row(
children: [
const SizedBox(width: 12),
Container(
constraints: const BoxConstraints(
maxWidth: 300.0, // 设置最大宽度为200.0
),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondaryContainer,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
bottomLeft: Radius.circular(6),
bottomRight: Radius.circular(16),
),
),
margin: const EdgeInsets.only(top: 12),
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(content['title'],
style: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(fontWeight: FontWeight.bold)),
Text(
Utils.dateFormat(item.timestamp),
style: Theme.of(context)
.textTheme
.labelSmall!
.copyWith(color: Theme.of(context).colorScheme.outline),
)
],
),
Divider(
color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
),
Text(
content['text'],
)
],
),
),
const Spacer(),
],
);
}
}
class SystemNotice2 extends StatelessWidget {
dynamic item;
SystemNotice2({super.key, this.item});
@override
Widget build(BuildContext context) {
Map content = item.content ?? '';
return Row(
children: [
const SizedBox(width: 12),
Container(
constraints: const BoxConstraints(
maxWidth: 300.0, // 设置最大宽度为200.0
),
margin: const EdgeInsets.only(top: 12),
padding: const EdgeInsets.only(bottom: 6),
child: NetworkImgLayer(
width: 320,
height: 150,
src: content['pic_url'],
),
),
const Spacer(),
],
);
}
}