feat: 评论区添加表情

This commit is contained in:
orz12
2024-02-23 01:35:27 +08:00
parent e78cd8b179
commit b44dbdfa09
5 changed files with 472 additions and 7 deletions

View File

@@ -0,0 +1,104 @@
import 'dart:async';
import 'package:PiliPalaX/models/user/my_emote.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import '../../../../../common/widgets/network_img_layer.dart';
import '../../../../../http/reply.dart';
class EmoteTab extends StatefulWidget {
final Function(String) onEmoteTap;
const EmoteTab({Key? key, required this.onEmoteTap}) : super(key: key);
@override
State<StatefulWidget> createState() => _EmoteTabState();
}
class _EmoteTabState extends State<EmoteTab> with TickerProviderStateMixin {
late TabController _myEmoteTabController;
late MyEmote myEmote;
late Future futureBuild;
Future getMyEmote() async {
var result = await ReplyHttp.getMyEmote(business: "reply");
if (result['status']) {
myEmote = MyEmote.fromJson(result['data']);
_myEmoteTabController = TabController(
length: myEmote.packages!.length,
initialIndex: myEmote.setting!.focusPkgId! - 1,
vsync: this);
} else {
SmartDialog.showToast(result['msg']);
myEmote = MyEmote();
}
return;
}
@override
void initState() {
super.initState();
futureBuild = getMyEmote();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: futureBuild,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
myEmote.packages != null) {
return Column(
children: [
Expanded(child: TabBarView(controller: _myEmoteTabController, children: [
for (Packages i in myEmote.packages!) ...<Widget>[
GridView.builder(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: i.type == 4 ? 100 : 36,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
mainAxisExtent: 36,
),
itemCount: i.emote!.length,
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
onTap: () {
widget.onEmoteTap(i.emote![index].text!);
},
child: i.type == 4
? Text(i.emote![index].text!,overflow: TextOverflow.clip,maxLines: 1,)
: NetworkImgLayer(
width: 36,
height: 36,
type: 'emote',
src: i.emote![index].url,
),
);
},
),
],
]),),
SizedBox(
height: 45,
child: TabBar(
isScrollable: true,
controller: _myEmoteTabController,
tabs: [
for (var i in myEmote.packages!)
NetworkImgLayer(
width: 36,
height: 36,
type: 'emote',
src: i.url,
),
],
))
],
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
);
}
}

View File

@@ -7,6 +7,9 @@ import 'package:PiliPalaX/models/common/reply_type.dart';
import 'package:PiliPalaX/models/video/reply/item.dart';
import 'package:PiliPalaX/utils/feed_back.dart';
import '../../../../common/constants.dart';
import '../reply/reply_emote/view.dart';
class VideoReplyNewDialog extends StatefulWidget {
final int? oid;
final int? root;
@@ -32,6 +35,7 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
final TextEditingController _replyContentController = TextEditingController();
final FocusNode replyContentFocusNode = FocusNode();
final GlobalKey _formKey = GlobalKey<FormState>();
bool isShowEmote = false;
@override
void initState() {
@@ -141,9 +145,13 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
width: 36,
height: 36,
child: IconButton(
onPressed: () {
onPressed: () async {
FocusScope.of(context)
.requestFocus(replyContentFocusNode);
await Future.delayed(const Duration(milliseconds: 200));
setState(() {
isShowEmote = false;
});
},
icon: Icon(Icons.keyboard,
size: 22,
@@ -154,7 +162,44 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
padding: MaterialStateProperty.all(EdgeInsets.zero),
backgroundColor:
MaterialStateProperty.resolveWith((states) {
return Theme.of(context).highlightColor;
if (states.contains(MaterialState.pressed) || !isShowEmote) {
return Theme.of(context).highlightColor;
}
// 默认状态下,返回透明颜色
return Colors.transparent;
}),
),
),
),
const SizedBox(
width: 10,
),
SizedBox(
width: 36,
height: 36,
child: IconButton(
onPressed: () {
//收起输入法
FocusScope.of(context).unfocus();
// 弹出表情选择
setState(() {
isShowEmote = true;
});
},
icon: Icon(Icons.emoji_emotions,
size: 22,
color: Theme.of(context).colorScheme.onBackground),
highlightColor:
Theme.of(context).colorScheme.onInverseSurface,
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
backgroundColor:
MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.pressed) || isShowEmote) {
return Theme.of(context).highlightColor;
}
// 默认状态下,返回透明颜色
return Colors.transparent;
}),
),
),
@@ -165,16 +210,42 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
],
),
),
AnimatedSize(
curve: Curves.easeInOut,
duration: const Duration(milliseconds: 300),
child: SizedBox(
if (!isShowEmote)
SizedBox(
width: double.infinity,
height: keyboardHeight,
),
),
if (isShowEmote)
SizedBox(
width: double.infinity,
height: 310,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: StyleString.safeSpace),
child: EmoteTab(
onEmoteTap: onEmoteTap,
),
),
)
],
),
);
}
void onEmoteTap(String emoteString) {
// 在光标处插入表情
final String currentText = _replyContentController.text;
final TextSelection selection = _replyContentController.selection;
final String newText = currentText.replaceRange(
selection.start,
selection.end,
emoteString,
);
_replyContentController.text = newText;
final int newCursorIndex = selection.start + emoteString.length;
_replyContentController.selection = selection.copyWith(
baseOffset: newCursorIndex,
extentOffset: newCursorIndex,
);
}
}