24
lib/common/widgets/custom_toast.dart
Normal file
24
lib/common/widgets/custom_toast.dart
Normal file
@ -0,0 +1,24 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomToast extends StatelessWidget {
|
||||
final String msg;
|
||||
const CustomToast({Key? key, required this.msg}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin:
|
||||
EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom + 20),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.primaryContainer.withOpacity(0.8),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Text(msg,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.copyWith(color: Theme.of(context).colorScheme.primary)),
|
||||
);
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ class StatDanMu extends StatelessWidget {
|
||||
size: 14,
|
||||
color: color,
|
||||
),
|
||||
const SizedBox(width: 3),
|
||||
const SizedBox(width: 2),
|
||||
Text(
|
||||
Utils.numFormat(danmu!),
|
||||
style: TextStyle(
|
||||
|
@ -24,7 +24,7 @@ class StatView extends StatelessWidget {
|
||||
size: 13,
|
||||
color: color,
|
||||
),
|
||||
const SizedBox(width: 3),
|
||||
const SizedBox(width: 2),
|
||||
Text(
|
||||
Utils.numFormat(view!),
|
||||
style: TextStyle(
|
||||
|
@ -4,6 +4,7 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:pilipala/common/widgets/custom_toast.dart';
|
||||
import 'package:pilipala/http/init.dart';
|
||||
import 'package:pilipala/pages/search/index.dart';
|
||||
import 'package:pilipala/pages/video/detail/index.dart';
|
||||
@ -61,7 +62,9 @@ class MyApp extends StatelessWidget {
|
||||
fallbackLocale: const Locale("zh", "CN"),
|
||||
getPages: Routes.getPages,
|
||||
home: const MainApp(),
|
||||
builder: FlutterSmartDialog.init(),
|
||||
builder: FlutterSmartDialog.init(
|
||||
toastBuilder: (String msg) => CustomToast(msg: msg),
|
||||
),
|
||||
navigatorObservers: [
|
||||
VideoDetailPage.routeObserver,
|
||||
SearchPage.routeObserver
|
||||
|
@ -9,6 +9,7 @@ import 'package:pilipala/models/common/dynamics_type.dart';
|
||||
import 'package:pilipala/models/dynamics/result.dart';
|
||||
import 'package:pilipala/models/dynamics/up.dart';
|
||||
import 'package:pilipala/models/live/item.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
|
||||
class DynamicsController extends GetxController {
|
||||
int page = 1;
|
||||
@ -78,6 +79,7 @@ class DynamicsController extends GetxController {
|
||||
}
|
||||
|
||||
pushDetail(item, floor, {action = 'all'}) async {
|
||||
feedBack();
|
||||
if (action == 'comment') {
|
||||
Get.toNamed('/dynamicDetail',
|
||||
arguments: {'item': item, 'floor': floor, 'action': action});
|
||||
|
@ -8,6 +8,7 @@ import 'package:pilipala/common/widgets/http_error.dart';
|
||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||
import 'package:pilipala/models/dynamics/result.dart';
|
||||
import 'package:pilipala/pages/mine/index.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
|
||||
import 'controller.dart';
|
||||
@ -143,6 +144,7 @@ class _DynamicsPageState extends State<DynamicsPage>
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
onValueChanged: (v) {
|
||||
feedBack();
|
||||
_dynamicsController.onSelectType(v);
|
||||
},
|
||||
),
|
||||
@ -155,7 +157,8 @@ class _DynamicsPageState extends State<DynamicsPage>
|
||||
bottom: 0,
|
||||
child: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: () => _dynamicsController.resetSearch(),
|
||||
onPressed: () =>
|
||||
{feedBack(), _dynamicsController.resetSearch()},
|
||||
icon: const Icon(Icons.history, size: 21),
|
||||
),
|
||||
),
|
||||
@ -167,15 +170,18 @@ class _DynamicsPageState extends State<DynamicsPage>
|
||||
alignment: Alignment.center,
|
||||
child: user.get(UserBoxKey.userLogin) ?? false
|
||||
? GestureDetector(
|
||||
onTap: () => showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (_) => const SizedBox(
|
||||
height: 450,
|
||||
child: MinePage(),
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
isScrollControlled: true,
|
||||
),
|
||||
onTap: () {
|
||||
feedBack();
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (_) => const SizedBox(
|
||||
height: 450,
|
||||
child: MinePage(),
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
isScrollControlled: true,
|
||||
);
|
||||
},
|
||||
child: NetworkImgLayer(
|
||||
type: 'avatar',
|
||||
width: 30,
|
||||
@ -184,15 +190,18 @@ class _DynamicsPageState extends State<DynamicsPage>
|
||||
),
|
||||
)
|
||||
: IconButton(
|
||||
onPressed: () => showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (_) => const SizedBox(
|
||||
height: 450,
|
||||
child: MinePage(),
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
isScrollControlled: true,
|
||||
),
|
||||
onPressed: () {
|
||||
feedBack();
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (_) => const SizedBox(
|
||||
height: 450,
|
||||
child: MinePage(),
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
isScrollControlled: true,
|
||||
);
|
||||
},
|
||||
icon: const Icon(CupertinoIcons.person, size: 22),
|
||||
),
|
||||
),
|
||||
|
@ -6,6 +6,7 @@ import 'package:get/get.dart';
|
||||
import 'package:pilipala/http/dynamics.dart';
|
||||
import 'package:pilipala/models/dynamics/result.dart';
|
||||
import 'package:pilipala/pages/dynamics/index.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
|
||||
class ActionPanel extends StatefulWidget {
|
||||
const ActionPanel({
|
||||
@ -31,6 +32,7 @@ class _ActionPanelState extends State<ActionPanel> {
|
||||
|
||||
// 动态点赞
|
||||
onLikeDynamic() async {
|
||||
feedBack();
|
||||
var item = widget.item!;
|
||||
String dynamicId = item.idStr!;
|
||||
// 1 已点赞 2 不喜欢 0 未操作
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
|
||||
Widget author(item, context) {
|
||||
@ -10,12 +11,16 @@ Widget author(item, context) {
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Get.toNamed(
|
||||
onTap: () {
|
||||
feedBack();
|
||||
Get.toNamed(
|
||||
'/member?mid=${item.modules.moduleAuthor.mid}',
|
||||
arguments: {
|
||||
'face': item.modules.moduleAuthor.face,
|
||||
'heroTag': heroTag
|
||||
}),
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Hero(
|
||||
tag: heroTag,
|
||||
child: NetworkImgLayer(
|
||||
|
@ -5,6 +5,7 @@ import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||
import 'package:pilipala/models/dynamics/up.dart';
|
||||
import 'package:pilipala/models/live/item.dart';
|
||||
import 'package:pilipala/pages/dynamics/controller.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
|
||||
@ -81,7 +82,7 @@ class _UpPanelState extends State<UpPanel> {
|
||||
),
|
||||
Material(
|
||||
child: InkWell(
|
||||
onTap: () => Get.toNamed('/follow'),
|
||||
onTap: () => {feedBack(), Get.toNamed('/follow')},
|
||||
child: Container(
|
||||
height: 100,
|
||||
padding: const EdgeInsets.only(left: 10, right: 10),
|
||||
@ -111,6 +112,7 @@ class _UpPanelState extends State<UpPanel> {
|
||||
bool isCurrent = currentMid == data.mid || currentMid == -1;
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
feedBack();
|
||||
if (data.type == 'up') {
|
||||
currentMid = data.mid;
|
||||
Get.find<DynamicsController>().mid.value = data.mid;
|
||||
@ -149,6 +151,7 @@ class _UpPanelState extends State<UpPanel> {
|
||||
}
|
||||
},
|
||||
onLongPress: () {
|
||||
feedBack();
|
||||
String heroTag = Utils.makeHeroTag(data.mid);
|
||||
Get.toNamed('/member?mid=${data.mid}',
|
||||
arguments: {'face': data.face, 'heroTag': heroTag});
|
||||
|
@ -1,13 +1,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
|
||||
Widget followItem({item}) {
|
||||
String heroTag = Utils.makeHeroTag(item!.mid);
|
||||
return ListTile(
|
||||
onTap: () => Get.toNamed('/member?mid=${item.mid}',
|
||||
arguments: {'face': item.face, 'heroTag': heroTag}),
|
||||
onTap: () {
|
||||
feedBack();
|
||||
Get.toNamed('/member?mid=${item.mid}',
|
||||
arguments: {'face': item.face, 'heroTag': heroTag});
|
||||
},
|
||||
leading: Hero(
|
||||
tag: heroTag,
|
||||
child: NetworkImgLayer(
|
||||
@ -19,6 +23,8 @@ Widget followItem({item}) {
|
||||
),
|
||||
title: Text(
|
||||
item.uname,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
subtitle: Text(
|
||||
|
@ -4,6 +4,7 @@ import 'package:get/get.dart';
|
||||
import 'package:pilipala/pages/hot/index.dart';
|
||||
import 'package:pilipala/pages/live/index.dart';
|
||||
import 'package:pilipala/pages/rcmd/index.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import './controller.dart';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
@ -92,7 +93,8 @@ class _HomePageState extends State<HomePage>
|
||||
dividerColor: Colors.transparent,
|
||||
unselectedLabelColor:
|
||||
Theme.of(context).colorScheme.outline,
|
||||
onTap: (value) => {_homeController.initialIndex = value},
|
||||
onTap: (value) =>
|
||||
{feedBack(), _homeController.initialIndex = value},
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -103,6 +105,7 @@ class _HomePageState extends State<HomePage>
|
||||
tag: 'searchTag',
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
feedBack();
|
||||
Get.toNamed('/search');
|
||||
},
|
||||
icon: const Icon(CupertinoIcons.search, size: 21),
|
||||
|
@ -3,6 +3,7 @@ import 'package:get/get.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:pilipala/pages/dynamics/index.dart';
|
||||
import 'package:pilipala/pages/home/index.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
import './controller.dart';
|
||||
|
||||
@ -44,6 +45,7 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
||||
}
|
||||
|
||||
void setIndex(int value) async {
|
||||
feedBack();
|
||||
if (selectedIndex != value) {
|
||||
selectedIndex = value;
|
||||
_animationController!.reverse().then((_) {
|
||||
|
@ -108,16 +108,19 @@ class _MemberPageState extends State<MemberPage>
|
||||
const SizedBox(height: 14),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
Expanded(
|
||||
child: Text(
|
||||
_memberController
|
||||
.memberInfo.value.name!,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyLarge!
|
||||
.copyWith(
|
||||
fontWeight:
|
||||
FontWeight.bold),
|
||||
),
|
||||
)),
|
||||
const SizedBox(width: 2),
|
||||
if (_memberController
|
||||
.memberInfo.value.sex ==
|
||||
|
@ -2,17 +2,22 @@ import 'package:get/get.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:pilipala/http/init.dart';
|
||||
import 'package:pilipala/pages/mine/controller.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
|
||||
class SettingController extends GetxController {
|
||||
Box user = GStrorage.user;
|
||||
RxBool userLogin = false.obs;
|
||||
Box userInfoCache = GStrorage.userInfo;
|
||||
Box setting = GStrorage.setting;
|
||||
RxBool feedBackEnable = false.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
userLogin.value = user.get(UserBoxKey.userLogin) ?? false;
|
||||
feedBackEnable.value =
|
||||
setting.get(SettingBoxKey.feedBackEnable, defaultValue: false);
|
||||
}
|
||||
|
||||
loginOut() async {
|
||||
@ -21,4 +26,11 @@ class SettingController extends GetxController {
|
||||
userLogin.value = user.get(UserBoxKey.userLogin) ?? false;
|
||||
userInfoCache.put('userInfoCache', null);
|
||||
}
|
||||
|
||||
// 开启关闭震动反馈
|
||||
onOpenFeedBack() {
|
||||
feedBack();
|
||||
feedBackEnable.value = !feedBackEnable.value;
|
||||
setting.put(SettingBoxKey.feedBackEnable, feedBackEnable.value);
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,10 @@ class SettingPage extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
TextStyle subTitleStyle = Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.copyWith(color: Theme.of(context).colorScheme.outline);
|
||||
final SettingController settingController = Get.put(SettingController());
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
@ -14,6 +18,28 @@ class SettingPage extends StatelessWidget {
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Obx(
|
||||
() => ListTile(
|
||||
enableFeedback: true,
|
||||
onTap: () => settingController.onOpenFeedBack(),
|
||||
title: const Text('震动反馈'),
|
||||
subtitle: Text('请确定手机设置中已开启震动反馈', style: subTitleStyle),
|
||||
trailing: Transform.scale(
|
||||
scale: 0.8,
|
||||
child: Switch(
|
||||
thumbIcon: MaterialStateProperty.resolveWith<Icon?>(
|
||||
(Set<MaterialState> states) {
|
||||
if (states.isNotEmpty &&
|
||||
states.first == MaterialState.selected) {
|
||||
return const Icon(Icons.done);
|
||||
}
|
||||
return null; // All other states will use the default thumbIcon.
|
||||
}),
|
||||
value: settingController.feedBackEnable.value,
|
||||
onChanged: (value) => settingController.onOpenFeedBack()),
|
||||
),
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => Visibility(
|
||||
visible: settingController.userLogin.value,
|
||||
|
@ -8,6 +8,7 @@ import 'package:pilipala/http/video.dart';
|
||||
import 'package:pilipala/models/user/fav_folder.dart';
|
||||
import 'package:pilipala/models/video_detail_res.dart';
|
||||
import 'package:pilipala/pages/video/detail/controller.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/id_utils.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
@ -303,6 +304,7 @@ class VideoIntroController extends GetxController {
|
||||
|
||||
// 选择文件夹
|
||||
onChoose(bool checkValue, int index) {
|
||||
feedBack();
|
||||
List<FavFolderItemData> datalist = favFolderData.value.list!;
|
||||
for (var i = 0; i < datalist.length; i++) {
|
||||
if (i == index) {
|
||||
@ -327,6 +329,7 @@ class VideoIntroController extends GetxController {
|
||||
|
||||
// 关注/取关up
|
||||
Future actionRelationMod() async {
|
||||
feedBack();
|
||||
if (user.get(UserBoxKey.userMid) == null) {
|
||||
SmartDialog.showToast('账号未登录');
|
||||
return;
|
||||
|
@ -12,6 +12,7 @@ import 'package:pilipala/common/widgets/stat/danmu.dart';
|
||||
import 'package:pilipala/common/widgets/stat/view.dart';
|
||||
import 'package:pilipala/models/video_detail_res.dart';
|
||||
import 'package:pilipala/pages/video/detail/introduction/controller.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
|
||||
@ -107,6 +108,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
sheetHeight = localCache.get('sheetHeight');
|
||||
}
|
||||
|
||||
// 收藏
|
||||
showFavBottomSheet() {
|
||||
if (videoIntroController.user.get(UserBoxKey.userMid) == null) {
|
||||
SmartDialog.showToast('账号未登录');
|
||||
@ -122,8 +124,21 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
);
|
||||
}
|
||||
|
||||
// 视频介绍
|
||||
showIntroDetail() {
|
||||
feedBack();
|
||||
showBottomSheet(
|
||||
context: context,
|
||||
enableDrag: true,
|
||||
builder: (BuildContext context) {
|
||||
return IntroDetail(videoDetail: widget.videoDetail!);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
ThemeData t = Theme.of(context);
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: StyleString.safeSpace, right: StyleString.safeSpace, top: 13),
|
||||
@ -133,15 +148,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
showBottomSheet(
|
||||
context: context,
|
||||
enableDrag: true,
|
||||
builder: (BuildContext context) {
|
||||
return IntroDetail(videoDetail: widget.videoDetail!);
|
||||
},
|
||||
);
|
||||
},
|
||||
onTap: () => showIntroDetail(),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
@ -168,9 +175,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
MaterialStateProperty.all(EdgeInsets.zero),
|
||||
backgroundColor:
|
||||
MaterialStateProperty.resolveWith((states) {
|
||||
return Theme.of(context)
|
||||
.highlightColor
|
||||
.withOpacity(0.2);
|
||||
return t.highlightColor.withOpacity(0.2);
|
||||
}),
|
||||
),
|
||||
onPressed: () {
|
||||
@ -189,46 +194,49 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Row(
|
||||
children: [
|
||||
const SizedBox(width: 2),
|
||||
StatView(
|
||||
theme: 'black',
|
||||
view: !widget.loadingStatus
|
||||
? widget.videoDetail!.stat!.view
|
||||
: videoItem['stat'].view,
|
||||
size: 'medium',
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
StatDanMu(
|
||||
theme: 'black',
|
||||
danmu: !widget.loadingStatus
|
||||
? widget.videoDetail!.stat!.danmaku
|
||||
: videoItem['stat'].danmaku,
|
||||
size: 'medium',
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
Utils.dateFormat(
|
||||
!widget.loadingStatus
|
||||
? widget.videoDetail!.pubdate
|
||||
: videoItem['pubdate'],
|
||||
formatType: 'detail'),
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
],
|
||||
GestureDetector(
|
||||
onTap: () => showIntroDetail(),
|
||||
child: Row(
|
||||
children: [
|
||||
StatView(
|
||||
theme: 'gray',
|
||||
view: !widget.loadingStatus
|
||||
? widget.videoDetail!.stat!.view
|
||||
: videoItem['stat'].view,
|
||||
size: 'medium',
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
StatDanMu(
|
||||
theme: 'gray',
|
||||
danmu: !widget.loadingStatus
|
||||
? widget.videoDetail!.stat!.danmaku
|
||||
: videoItem['stat'].danmaku,
|
||||
size: 'medium',
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
Utils.dateFormat(
|
||||
!widget.loadingStatus
|
||||
? widget.videoDetail!.pubdate
|
||||
: videoItem['pubdate'],
|
||||
formatType: 'detail'),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: t.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
// 点赞收藏转发
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 15),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: actionRow(
|
||||
context,
|
||||
videoIntroController,
|
||||
videoDetailCtr,
|
||||
),
|
||||
SingleChildScrollView(
|
||||
padding: const EdgeInsets.only(top: 7, bottom: 7),
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: actionRow(
|
||||
context,
|
||||
videoIntroController,
|
||||
videoDetailCtr,
|
||||
),
|
||||
),
|
||||
// 合集
|
||||
@ -237,122 +245,106 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
seasonPanel(widget.videoDetail!.ugcSeason!,
|
||||
widget.videoDetail!.pages!.first.cid, sheetHeight)
|
||||
],
|
||||
// Divider(
|
||||
// height: 26,
|
||||
// color: Theme.of(context).dividerColor.withOpacity(0.1),
|
||||
// ),
|
||||
const SizedBox(height: 20),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
int mid = !widget.loadingStatus
|
||||
? widget.videoDetail!.owner!.mid
|
||||
: videoItem['owner'].mid;
|
||||
String face = !widget.loadingStatus
|
||||
? widget.videoDetail!.owner!.face
|
||||
: videoItem['owner'].face;
|
||||
Get.toNamed('/member?mid=$mid', arguments: {
|
||||
'face': face,
|
||||
'heroTag': (mid + 99).toString()
|
||||
});
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(width: 5),
|
||||
NetworkImgLayer(
|
||||
type: 'avatar',
|
||||
src: !widget.loadingStatus
|
||||
? widget.videoDetail!.owner!.face
|
||||
: videoItem['owner'].face,
|
||||
width: 34,
|
||||
height: 34,
|
||||
fadeInDuration: Duration.zero,
|
||||
fadeOutDuration: Duration.zero,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
!widget.loadingStatus
|
||||
? widget.videoDetail!.owner!.name
|
||||
: videoItem['owner'].name,
|
||||
style: const TextStyle(fontSize: 13),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
widget.loadingStatus
|
||||
? '- 粉丝'
|
||||
: Utils.numFormat(
|
||||
videoIntroController.userStat['follower']),
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.labelSmall!
|
||||
.fontSize,
|
||||
color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
const Spacer(),
|
||||
AnimatedOpacity(
|
||||
opacity: widget.loadingStatus ? 0 : 1,
|
||||
duration: const Duration(milliseconds: 150),
|
||||
child: SizedBox(
|
||||
height: 32,
|
||||
child: Obx(
|
||||
() => videoIntroController.followStatus.isNotEmpty
|
||||
? TextButton(
|
||||
onPressed: () => videoIntroController
|
||||
.actionRelationMod(),
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 8, right: 8),
|
||||
foregroundColor:
|
||||
videoIntroController.followStatus[
|
||||
'attribute'] !=
|
||||
0
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.outline
|
||||
: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimary,
|
||||
backgroundColor:
|
||||
videoIntroController.followStatus[
|
||||
'attribute'] !=
|
||||
0
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.onInverseSurface
|
||||
: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary, // 设置按钮背景色
|
||||
),
|
||||
child: Text(
|
||||
videoIntroController.followStatus[
|
||||
'attribute'] !=
|
||||
0
|
||||
? '已关注'
|
||||
: '关注',
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.fontSize),
|
||||
),
|
||||
)
|
||||
: ElevatedButton(
|
||||
onPressed: () => videoIntroController
|
||||
.actionRelationMod(),
|
||||
child: const Text('关注'),
|
||||
),
|
||||
onTap: () {
|
||||
feedBack();
|
||||
int mid = !widget.loadingStatus
|
||||
? widget.videoDetail!.owner!.mid
|
||||
: videoItem['owner'].mid;
|
||||
String face = !widget.loadingStatus
|
||||
? widget.videoDetail!.owner!.face
|
||||
: videoItem['owner'].face;
|
||||
Get.toNamed('/member?mid=$mid', arguments: {
|
||||
'face': face,
|
||||
'heroTag': (mid + 99).toString()
|
||||
});
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 12, bottom: 12, left: 4, right: 4),
|
||||
child: Row(
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
type: 'avatar',
|
||||
src: !widget.loadingStatus
|
||||
? widget.videoDetail!.owner!.face
|
||||
: videoItem['owner'].face,
|
||||
width: 34,
|
||||
height: 34,
|
||||
fadeInDuration: Duration.zero,
|
||||
fadeOutDuration: Duration.zero,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
!widget.loadingStatus
|
||||
? widget.videoDetail!.owner!.name
|
||||
: videoItem['owner'].name,
|
||||
style: const TextStyle(fontSize: 13),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
widget.loadingStatus
|
||||
? '-'
|
||||
: Utils.numFormat(videoIntroController
|
||||
.userStat['follower']),
|
||||
style: TextStyle(
|
||||
fontSize: t.textTheme.labelSmall!.fontSize,
|
||||
color: t.colorScheme.outline),
|
||||
),
|
||||
const Spacer(),
|
||||
AnimatedOpacity(
|
||||
opacity: widget.loadingStatus ? 0 : 1,
|
||||
duration: const Duration(milliseconds: 150),
|
||||
child: SizedBox(
|
||||
height: 32,
|
||||
child: Obx(
|
||||
() => videoIntroController
|
||||
.followStatus.isNotEmpty
|
||||
? TextButton(
|
||||
onPressed: () => videoIntroController
|
||||
.actionRelationMod(),
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 8, right: 8),
|
||||
foregroundColor:
|
||||
videoIntroController
|
||||
.followStatus[
|
||||
'attribute'] !=
|
||||
0
|
||||
? t.colorScheme.outline
|
||||
: t.colorScheme.onPrimary,
|
||||
backgroundColor:
|
||||
videoIntroController
|
||||
.followStatus[
|
||||
'attribute'] !=
|
||||
0
|
||||
? t.colorScheme
|
||||
.onInverseSurface
|
||||
: t.colorScheme
|
||||
.primary, // 设置按钮背景色
|
||||
),
|
||||
child: Text(
|
||||
videoIntroController.followStatus[
|
||||
'attribute'] !=
|
||||
0
|
||||
? '已关注'
|
||||
: '关注',
|
||||
style: TextStyle(
|
||||
fontSize: t.textTheme
|
||||
.labelMedium!.fontSize),
|
||||
),
|
||||
)
|
||||
: ElevatedButton(
|
||||
onPressed: () => videoIntroController
|
||||
.actionRelationMod(),
|
||||
child: const Text('关注'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 4)
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
// Divider(
|
||||
// height: 12,
|
||||
// color: Theme.of(context).dividerColor.withOpacity(0.1),
|
||||
// ),
|
||||
)),
|
||||
],
|
||||
)
|
||||
: const SizedBox(
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
|
||||
class ActionRowItem extends StatelessWidget {
|
||||
final Icon? icon;
|
||||
@ -27,7 +28,10 @@ class ActionRowItem extends StatelessWidget {
|
||||
borderRadius: const BorderRadius.all(Radius.circular(30)),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: InkWell(
|
||||
onTap: () => onTap!(),
|
||||
onTap: () => {
|
||||
feedBack(),
|
||||
onTap!(),
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(13, 6.5, 15, 6.3),
|
||||
child: Row(
|
||||
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:pilipala/common/widgets/http_error.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
|
||||
class FavPanel extends StatefulWidget {
|
||||
@ -43,6 +44,7 @@ class _FavPanelState extends State<FavPanel> {
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
feedBack();
|
||||
await widget.ctr!.actionFavVideo();
|
||||
},
|
||||
child: const Text('完成'),
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
|
||||
class MenuRow extends StatelessWidget {
|
||||
final bool? loadingStatus;
|
||||
@ -62,7 +63,10 @@ class MenuRow extends StatelessWidget {
|
||||
borderRadius: const BorderRadius.all(Radius.circular(30)),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: InkWell(
|
||||
onTap: () => onTap!(),
|
||||
onTap: () => {
|
||||
feedBack(),
|
||||
onTap!(),
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.fromLTRB(13, 5.5, 13, 5.5),
|
||||
decoration: BoxDecoration(
|
||||
|
@ -4,6 +4,7 @@ import 'package:pilipala/http/reply.dart';
|
||||
import 'package:pilipala/models/common/reply_sort_type.dart';
|
||||
import 'package:pilipala/models/common/reply_type.dart';
|
||||
import 'package:pilipala/models/video/reply/item.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
|
||||
class VideoReplyController extends GetxController {
|
||||
VideoReplyController(
|
||||
@ -83,6 +84,7 @@ class VideoReplyController extends GetxController {
|
||||
|
||||
// 排序搜索评论
|
||||
queryBySort() {
|
||||
feedBack();
|
||||
switch (sortType) {
|
||||
case ReplySortType.time:
|
||||
sortType = ReplySortType.like;
|
||||
|
@ -8,6 +8,7 @@ import 'package:pilipala/common/widgets/http_error.dart';
|
||||
import 'package:pilipala/models/common/reply_type.dart';
|
||||
import 'package:pilipala/pages/video/detail/index.dart';
|
||||
import 'package:pilipala/pages/video/detail/replyNew/index.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/id_utils.dart';
|
||||
import 'controller.dart';
|
||||
import 'widgets/reply_item.dart';
|
||||
@ -262,6 +263,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
||||
child: FloatingActionButton(
|
||||
heroTag: null,
|
||||
onPressed: () {
|
||||
feedBack();
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
|
@ -7,6 +7,7 @@ import 'package:pilipala/models/common/reply_type.dart';
|
||||
import 'package:pilipala/models/video/reply/item.dart';
|
||||
import 'package:pilipala/pages/video/detail/controller.dart';
|
||||
import 'package:pilipala/pages/video/detail/replyNew/index.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
|
||||
import 'zan.dart';
|
||||
@ -86,6 +87,7 @@ class ReplyItem extends StatelessWidget {
|
||||
// 头像、昵称
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
feedBack();
|
||||
Get.toNamed('/member?mid=${replyItem!.mid}', arguments: {
|
||||
'face': replyItem!.member!.avatar!,
|
||||
'heroTag': heroTag
|
||||
@ -259,6 +261,7 @@ class ReplyItem extends StatelessWidget {
|
||||
.labelMedium!
|
||||
.copyWith(color: Theme.of(context).colorScheme.outline)),
|
||||
onPressed: () {
|
||||
feedBack();
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
@ -348,6 +351,7 @@ class ReplyItemRow extends StatelessWidget {
|
||||
),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
feedBack();
|
||||
String heroTag =
|
||||
Utils.makeHeroTag(replies![i].member.mid);
|
||||
Get.toNamed(
|
||||
|
@ -4,6 +4,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:pilipala/http/reply.dart';
|
||||
import 'package:pilipala/models/common/reply_type.dart';
|
||||
import 'package:pilipala/models/video/reply/item.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
|
||||
class ZanButton extends StatefulWidget {
|
||||
const ZanButton({
|
||||
@ -22,6 +23,8 @@ class ZanButton extends StatefulWidget {
|
||||
class _ZanButtonState extends State<ZanButton> {
|
||||
// 评论点赞
|
||||
onLikeReply() async {
|
||||
feedBack();
|
||||
SmartDialog.showLoading(msg: 'pilipala ...');
|
||||
ReplyItemModel replyItem = widget.replyItem!;
|
||||
int oid = replyItem.oid!;
|
||||
int rpid = replyItem.rpid!;
|
||||
@ -29,8 +32,9 @@ class _ZanButtonState extends State<ZanButton> {
|
||||
int action = replyItem.action == 0 ? 1 : 0;
|
||||
var res = await ReplyHttp.likeReply(
|
||||
type: widget.replyType!.index, oid: oid, rpid: rpid, action: action);
|
||||
SmartDialog.dismiss();
|
||||
if (res['status']) {
|
||||
SmartDialog.showToast(replyItem.action == 0 ? '点赞成功' : '取消赞');
|
||||
SmartDialog.showToast(replyItem.action == 0 ? '点赞成功 👍' : '取消赞 💔');
|
||||
if (action == 1) {
|
||||
replyItem.like = replyItem.like! + 1;
|
||||
replyItem.action = 1;
|
||||
|
@ -6,6 +6,7 @@ import 'package:hive/hive.dart';
|
||||
import 'package:pilipala/http/video.dart';
|
||||
import 'package:pilipala/models/common/reply_type.dart';
|
||||
import 'package:pilipala/models/video/reply/item.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
|
||||
class VideoReplyNewDialog extends StatefulWidget {
|
||||
@ -68,6 +69,7 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
|
||||
}
|
||||
|
||||
Future submitReplyAdd() async {
|
||||
feedBack();
|
||||
String message = _replyContentController.text;
|
||||
var result = await VideoHttp.replyAdd(
|
||||
type: widget.replyType ?? ReplyType.video,
|
||||
|
12
lib/utils/feed_back.dart
Normal file
12
lib/utils/feed_back.dart
Normal file
@ -0,0 +1,12 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
|
||||
Box setting = GStrorage.setting;
|
||||
void feedBack() {
|
||||
// 设置中是否开启
|
||||
bool enable = setting.get(SettingBoxKey.feedBackEnable, defaultValue: false);
|
||||
if (enable) {
|
||||
HapticFeedback.lightImpact();
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ class GStrorage {
|
||||
static late final Box hotKeyword;
|
||||
static late final Box historyword;
|
||||
static late final Box localCache;
|
||||
static late final Box setting;
|
||||
|
||||
static Future<void> init() async {
|
||||
final dir = await getApplicationDocumentsDirectory();
|
||||
@ -27,6 +28,8 @@ class GStrorage {
|
||||
userInfo = await Hive.openBox('userInfo');
|
||||
// 本地缓存
|
||||
localCache = await Hive.openBox('localCache');
|
||||
// 设置
|
||||
setting = await Hive.openBox('setting');
|
||||
}
|
||||
|
||||
static regAdapter() {
|
||||
@ -61,6 +64,7 @@ class UserBoxKey {
|
||||
|
||||
class SettingBoxKey {
|
||||
static const String themeMode = 'themeMode';
|
||||
static const String feedBackEnable = 'feedBackEnable';
|
||||
}
|
||||
|
||||
class LocalCacheKey {
|
||||
|
Reference in New Issue
Block a user