Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-01-15 18:19:51 +08:00
parent 25148509d2
commit d2f8aff421
21 changed files with 217 additions and 142 deletions

View File

@@ -0,0 +1,63 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
Future<TimeOfDay?> showTimePicker({
required BuildContext context,
required TimeOfDay initialTime,
TransitionBuilder? builder,
bool barrierDismissible = true,
Color? barrierColor,
String? barrierLabel,
bool useRootNavigator = true,
TimePickerEntryMode initialEntryMode = TimePickerEntryMode.dial,
String? cancelText,
String? confirmText,
String? helpText,
String? errorInvalidText,
String? hourLabelText,
String? minuteLabelText,
RouteSettings? routeSettings,
EntryModeChangeCallback? onEntryModeChanged,
Offset? anchorPoint,
Orientation? orientation,
Icon? switchToInputEntryModeIcon,
Icon? switchToTimerEntryModeIcon,
bool emptyInitialInput = false,
BoxConstraints? constraints,
}) {
assert(debugCheckHasMaterialLocalizations(context));
final Widget dialog = DialogTheme(
data: const DialogThemeData(constraints: BoxConstraints(minWidth: 280.0)),
child: TimePickerDialog(
initialTime: initialTime,
initialEntryMode: initialEntryMode,
cancelText: cancelText,
confirmText: confirmText,
helpText: helpText,
errorInvalidText: errorInvalidText,
hourLabelText: hourLabelText,
minuteLabelText: minuteLabelText,
orientation: orientation,
onEntryModeChanged: onEntryModeChanged,
switchToInputEntryModeIcon: switchToInputEntryModeIcon,
switchToTimerEntryModeIcon: switchToTimerEntryModeIcon,
emptyInitialInput: emptyInitialInput,
),
);
return showDialog<TimeOfDay>(
context: context,
barrierDismissible: barrierDismissible,
barrierColor: barrierColor,
barrierLabel: barrierLabel,
useRootNavigator: useRootNavigator,
builder: (BuildContext context) {
return builder == null ? dialog : builder(context, dialog);
},
routeSettings: routeSettings,
anchorPoint: anchorPoint,
);
}

View File

@@ -327,7 +327,12 @@ class _CustomGridViewDelegate extends MultiChildLayoutDelegate {
@override
void performLayout(Size size) {
final constraints = BoxConstraints.expand(width: width, height: height);
final constraints = BoxConstraints(
minWidth: width,
maxWidth: width,
minHeight: height,
maxHeight: height,
);
for (int i = 0; i < itemCount; i++) {
layoutChild(i, constraints);
positionChild(

View File

@@ -124,9 +124,11 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
..removeListener(listener)
..dispose();
_transformationController.dispose();
for (final item in widget.sources) {
if (item.sourceType == SourceType.networkImage) {
CachedNetworkImageProvider(_getActualUrl(item.url)).evict();
if (widget.quality != _quality) {
for (final item in widget.sources) {
if (item.sourceType == SourceType.networkImage) {
CachedNetworkImageProvider(_getActualUrl(item.url)).evict();
}
}
}
super.dispose();

View File

@@ -98,7 +98,7 @@ class ActionPanel extends StatelessWidget {
},
child: Text(
like.count != null ? NumUtils.numFormat(like.count) : '点赞',
key: ValueKey<String>(like.count?.toString() ?? '点赞'),
key: ValueKey<int?>(like.count),
style: TextStyle(color: like.status! ? primary : outline),
),
),

View File

@@ -8,6 +8,7 @@ import 'package:PiliPlus/common/widgets/flutter/draggable_sheet/draggable_scroll
as dyn_sheet;
import 'package:PiliPlus/common/widgets/flutter/text_field/controller.dart';
import 'package:PiliPlus/common/widgets/flutter/text_field/text_field.dart';
import 'package:PiliPlus/common/widgets/flutter/time_picker.dart';
import 'package:PiliPlus/common/widgets/pair.dart';
import 'package:PiliPlus/http/dynamics.dart';
import 'package:PiliPlus/http/loading_state.dart';
@@ -31,7 +32,8 @@ import 'package:PiliPlus/utils/extension/context_ext.dart';
import 'package:PiliPlus/utils/extension/iterable_ext.dart';
import 'package:PiliPlus/utils/grid.dart';
import 'package:PiliPlus/utils/request_utils.dart';
import 'package:flutter/material.dart' hide DraggableScrollableSheet;
import 'package:flutter/material.dart'
hide DraggableScrollableSheet, showTimePicker;
import 'package:flutter/services.dart' show LengthLimitingTextInputFormatter;
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
@@ -280,46 +282,45 @@ class _CreateDynPanelState extends CommonRichTextPubPageState<CreateDynPanel> {
Widget _buildImageList(ThemeData theme) => SizedBox(
height: 100,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Obx(
() => Row(
spacing: 10,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...List.generate(
imageList.length,
(index) => buildImage(index, 100),
),
if (imageList.length != limit)
Builder(
builder: (context) {
const borderRadius = StyleString.mdRadius;
return Material(
borderRadius: borderRadius,
child: InkWell(
borderRadius: borderRadius,
onTap: () => onPickImage(() {
if (imageList.isNotEmpty && !enablePublish.value) {
enablePublish.value = true;
}
}),
child: Ink(
width: 100,
height: 100,
decoration: BoxDecoration(
borderRadius: borderRadius,
color: theme.colorScheme.secondaryContainer,
),
child: const Center(child: Icon(Icons.add, size: 35)),
),
),
);
},
child: Obx(
() => CustomScrollView(
scrollDirection: Axis.horizontal,
slivers: [
const SliverToBoxAdapter(child: SizedBox(width: 16)),
if (imageList.isNotEmpty)
SliverPadding(
padding: const .only(right: 10),
sliver: SliverList.separated(
itemCount: imageList.length,
itemBuilder: (context, index) => buildImage(index, 100),
separatorBuilder: (_, _) => const SizedBox(width: 10),
),
],
),
),
if (imageList.length != limit)
SliverToBoxAdapter(
child: Material(
borderRadius: StyleString.mdRadius,
child: InkWell(
borderRadius: StyleString.mdRadius,
onTap: () => onPickImage(() {
if (imageList.isNotEmpty && !enablePublish.value) {
enablePublish.value = true;
}
}),
child: Ink(
width: 100,
height: 100,
decoration: BoxDecoration(
borderRadius: StyleString.mdRadius,
color: theme.colorScheme.secondaryContainer,
),
child: const Center(child: Icon(Icons.add, size: 35)),
),
),
),
),
const SliverToBoxAdapter(child: SizedBox(width: 16)),
],
),
),
);
@@ -629,18 +630,18 @@ class _CreateDynPanelState extends CommonRichTextPubPageState<CreateDynPanel> {
}
final color = theme.colorScheme.onSurfaceVariant;
late final gridDelegate = SliverGridDelegateWithExtentAndRatio(
maxCrossAxisExtent: 65,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
mainAxisExtent: 25,
);
return SizedBox(
height: height,
child: GridView(
physics: const ClampingScrollPhysics(),
padding: const EdgeInsets.only(left: 12, bottom: 12, right: 12),
gridDelegate: gridDelegate,
gridDelegate: SliverGridDelegateWithExtentAndRatio(
maxCrossAxisExtent: 65,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
mainAxisExtent: 25,
),
children: [
item(
onTap: _onReserve,

View File

@@ -1,7 +1,8 @@
import 'package:PiliPlus/common/widgets/flutter/time_picker.dart';
import 'package:PiliPlus/pages/dynamics_create_reserve/controller.dart';
import 'package:PiliPlus/utils/date_utils.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter/material.dart' hide showTimePicker;
import 'package:flutter/services.dart'
show TextInputFormatter, LengthLimitingTextInputFormatter;
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';

View File

@@ -1,6 +1,7 @@
import 'dart:io' show File;
import 'package:PiliPlus/common/widgets/button/icon_button.dart';
import 'package:PiliPlus/common/widgets/flutter/time_picker.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/models/dynamics/vote_model.dart';
import 'package:PiliPlus/pages/dynamics_create_vote/controller.dart';
@@ -9,7 +10,7 @@ import 'package:PiliPlus/utils/extension/file_ext.dart';
import 'package:PiliPlus/utils/platform_utils.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:flutter/material.dart' hide showTimePicker;
import 'package:flutter/services.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';

View File

@@ -69,7 +69,7 @@ class DynamicDetailController extends CommonDynController {
action: action,
);
if (res.isSuccess) {
await Future.delayed(const Duration(milliseconds: 500), () {
Future.delayed(const Duration(milliseconds: 500), () {
if (!isClosed) {
onReload();
}

View File

@@ -68,6 +68,7 @@ class _EmotePanelState extends State<EmotePanel>
final size = flag ? 40.0 : 60.0;
final isTextEmote = e.type == 4;
return GridView.builder(
physics: const ClampingScrollPhysics(),
padding: const EdgeInsets.only(
left: 12,
right: 12,

View File

@@ -174,9 +174,11 @@ class _CreateFavPageState extends State<CreateFavPage> {
final leadingStyle = const TextStyle(fontSize: 14);
Widget _buildBody(ThemeData theme) => SingleChildScrollView(
padding: .only(bottom: MediaQuery.viewPaddingOf(context).bottom + 25),
child: Column(
spacing: 12,
children: [
if (_attr == null || !FavUtils.isDefaultFav(_attr!)) ...[
if (_attr == null || !FavUtils.isDefaultFav(_attr!))
Builder(
builder: (context) {
return ListTile(
@@ -260,8 +262,6 @@ class _CreateFavPageState extends State<CreateFavPage> {
);
},
),
const SizedBox(height: 16),
],
ListTile(
tileColor: theme.colorScheme.onInverseSurface,
title: Row(
@@ -318,8 +318,7 @@ class _CreateFavPageState extends State<CreateFavPage> {
],
),
),
const SizedBox(height: 16),
if (_attr == null || !FavUtils.isDefaultFav(_attr!)) ...[
if (_attr == null || !FavUtils.isDefaultFav(_attr!))
ListTile(
tileColor: theme.colorScheme.onInverseSurface,
title: Row(
@@ -362,8 +361,6 @@ class _CreateFavPageState extends State<CreateFavPage> {
],
),
),
const SizedBox(height: 16),
],
Builder(
builder: (context) {
void onTap() {
@@ -389,7 +386,6 @@ class _CreateFavPageState extends State<CreateFavPage> {
);
},
),
const SizedBox(height: 16),
],
),
);

View File

@@ -75,6 +75,7 @@ class _LiveEmotePanelState extends State<LiveEmotePanel>
final width = widthFac * 38;
final height = heightFac * 38;
return GridView.builder(
physics: const ClampingScrollPhysics(),
padding: const EdgeInsets.only(
left: 12,
right: 12,

View File

@@ -66,13 +66,17 @@ class MemberDynamicsController
: DynamicsHttp.setTop(dynamicId: dynamicId));
if (res.isSuccess) {
List<DynamicItemModel> list = loadingState.value.data!;
list[0].modules.moduleTag = null;
list[0].modules
..moduleTag = null
..moduleAuthor?.isTop = false;
if (isTop) {
loadingState.refresh();
SmartDialog.showToast('取消置顶成功');
} else {
final item = list.firstWhere((item) => item.idStr == dynamicId);
item.modules.moduleTag = ModuleTag(text: '置顶');
item.modules
..moduleTag = ModuleTag(text: '置顶')
..moduleAuthor?.isTop = true;
list
..remove(item)
..insert(0, item);

View File

@@ -20,13 +20,14 @@ class _ExtraSettingState extends State<ExtraSetting> {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: showAppBar ? AppBar(title: const Text('其它设置')) : null,
body: ListView(
body: ListView.builder(
padding: EdgeInsets.only(
left: showAppBar ? padding.left : 0,
right: showAppBar ? padding.right : 0,
bottom: padding.bottom + 100,
),
children: settings.map((item) => item.widget).toList(),
itemCount: settings.length,
itemBuilder: (context, index) => settings[index].widget,
),
);
}

View File

@@ -20,13 +20,14 @@ class _PlaySettingState extends State<PlaySetting> {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: showAppBar ? AppBar(title: const Text('播放器设置')) : null,
body: ListView(
body: ListView.builder(
padding: EdgeInsets.only(
left: showAppBar ? padding.left : 0,
right: showAppBar ? padding.right : 0,
bottom: padding.bottom + 100,
),
children: settings.map((item) => item.widget).toList(),
itemCount: settings.length,
itemBuilder: (context, index) => settings[index].widget,
),
);
}

View File

@@ -20,13 +20,14 @@ class _StyleSettingState extends State<StyleSetting> {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: showAppBar ? AppBar(title: const Text('外观设置')) : null,
body: ListView(
body: ListView.builder(
padding: EdgeInsets.only(
left: showAppBar ? padding.left : 0,
right: showAppBar ? padding.right : 0,
bottom: padding.bottom + 100,
),
children: settings.map((item) => item.widget).toList(),
itemCount: settings.length,
itemBuilder: (context, index) => settings[index].widget,
),
);
}

View File

@@ -20,13 +20,14 @@ class _VideoSettingState extends State<VideoSetting> {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: showAppBar ? AppBar(title: const Text('音视频设置')) : null,
body: ListView(
body: ListView.builder(
padding: EdgeInsets.only(
left: showAppBar ? padding.left : 0,
right: showAppBar ? padding.right : 0,
bottom: padding.bottom + 100,
),
children: settings.map((item) => item.widget).toList(),
itemCount: settings.length,
itemBuilder: (context, index) => settings[index].widget,
),
);
}

View File

@@ -247,7 +247,7 @@ class _CdnSelectDialogState extends State<CdnSelectDialog> {
valueListenable: item,
builder: (context, value, _) {
return Text(
item.value ?? '---',
value ?? '---',
style: const TextStyle(fontSize: 13),
maxLines: 1,
overflow: TextOverflow.ellipsis,

View File

@@ -66,7 +66,7 @@ class _SettingsSearchPageState
onPressed: () {
if (_textEditingController.text.isNotEmpty) {
_textEditingController.clear();
_list.value = <SettingsModel>[];
_list.clear();
} else {
Get.back();
}

View File

@@ -82,7 +82,7 @@ class _PayCoinsPageState extends State<PayCoinsPage>
Timer? _timer;
late final RxInt _thunderIndex = (-1).obs;
late final List<String> _thunderImages = const [
static const List<String> _thunderImages = [
'assets/images/paycoins/ic_thunder_1.png',
'assets/images/paycoins/ic_thunder_2.png',
'assets/images/paycoins/ic_thunder_3.png',

View File

@@ -115,17 +115,12 @@ class _ReplyPageState extends CommonRichTextPubPageState<ReplyPage> {
if (imageList.isNotEmpty) {
return SizedBox(
height: 85,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.fromLTRB(15, 0, 15, 10),
child: Row(
spacing: 10,
crossAxisAlignment: CrossAxisAlignment.start,
children: List.generate(
imageList.length,
(index) => buildImage(index, 75),
),
),
child: ListView.separated(
scrollDirection: .horizontal,
padding: const .fromLTRB(15, 0, 15, 10),
itemCount: imageList.length,
itemBuilder: (_, index) => buildImage(index, 75),
separatorBuilder: (_, _) => const SizedBox(width: 10),
),
);
} else {
@@ -314,6 +309,7 @@ class _ReplyPageState extends CommonRichTextPubPageState<ReplyPage> {
return SizedBox(
height: height,
child: GridView(
physics: const ClampingScrollPhysics(),
padding: const EdgeInsets.only(left: 12, bottom: 12, right: 12),
gridDelegate: gridDelegate,
children: [

View File

@@ -179,62 +179,62 @@ class _SendDanmakuPanelState extends CommonTextPubPageState<SendDanmakuPanel> {
),
),
),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 12),
Row(
children: [
Text(
'弹幕字号',
style: TextStyle(
fontSize: 15,
color: themeData.colorScheme.onSurface,
),
),
const SizedBox(width: 16),
_buildFontSizeItem(18, ''),
const SizedBox(width: 5),
_buildFontSizeItem(25, '标准'),
],
),
const SizedBox(height: 12),
Row(
children: [
Text(
'弹幕样式',
style: TextStyle(
fontSize: 15,
color: themeData.colorScheme.onSurface,
),
),
const SizedBox(width: 16),
_buildPositionItem(1, '滚动'),
const SizedBox(width: 5),
_buildPositionItem(5, '顶部'),
const SizedBox(width: 5),
_buildPositionItem(4, '底部'),
],
),
const SizedBox(height: 12),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'弹幕颜色',
style: TextStyle(
fontSize: 15,
color: themeData.colorScheme.onSurface,
),
),
const SizedBox(width: 16),
_buildColorPanel,
],
),
SizedBox(height: 12 + MediaQuery.viewPaddingOf(context).bottom),
],
child: ListView(
physics: const ClampingScrollPhysics(),
padding: .only(
top: 12,
bottom: 12 + MediaQuery.viewPaddingOf(context).bottom,
),
children: [
Row(
children: [
Text(
'弹幕字号',
style: TextStyle(
fontSize: 15,
color: themeData.colorScheme.onSurface,
),
),
const SizedBox(width: 16),
_buildFontSizeItem(18, ''),
const SizedBox(width: 5),
_buildFontSizeItem(25, '标准'),
],
),
const SizedBox(height: 12),
Row(
children: [
Text(
'弹幕样式',
style: TextStyle(
fontSize: 15,
color: themeData.colorScheme.onSurface,
),
),
const SizedBox(width: 16),
_buildPositionItem(1, '滚动'),
const SizedBox(width: 5),
_buildPositionItem(5, '顶部'),
const SizedBox(width: 5),
_buildPositionItem(4, '底部'),
],
),
const SizedBox(height: 12),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'弹幕颜色',
style: TextStyle(
fontSize: 15,
color: themeData.colorScheme.onSurface,
),
),
const SizedBox(width: 16),
_buildColorPanel,
],
),
],
),
);