mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-06-18 08:20:12 +08:00
27
README.md
27
README.md
@@ -22,22 +22,18 @@
|
||||
<br/>
|
||||
</div>
|
||||
|
||||
## 开发环境
|
||||
|
||||
```bash
|
||||
[✓] Flutter (Channel stable, 3.24.0, on Microsoft Windows [版本 10.0.19045.4046], locale zh-CN)
|
||||
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
|
||||
[✓] Xcode - develop for iOS and macOS (Xcode 15.1)
|
||||
[✓] Chrome - develop for the web
|
||||
[✓] Android Studio (version 2022.3)
|
||||
[✓] VS Code (version 1.85.1)
|
||||
[✓] Connected device (3 available)
|
||||
[✓] Network resources
|
||||
|
||||
```
|
||||
|
||||
<br/>
|
||||
|
||||
## 适配平台
|
||||
|
||||
- [x] Android
|
||||
- [x] iOS
|
||||
- [x] Pad
|
||||
- [x] Windows
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
## refactor
|
||||
|
||||
@@ -136,11 +132,6 @@
|
||||
|
||||
## 功能
|
||||
|
||||
目前着重移动端(Android、iOS)和Pad端,暂时没有适配桌面端、手表端等
|
||||
|
||||
<br/>
|
||||
|
||||
|
||||
- [x] 推荐视频列表(app端)
|
||||
- [x] 最热视频列表
|
||||
- [x] 热门直播
|
||||
|
||||
BIN
assets/images/logo/logo_large.png
Normal file
BIN
assets/images/logo/logo_large.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
@@ -42,9 +42,11 @@ class _EmotePanelState extends State<EmotePanel>
|
||||
ThemeData theme,
|
||||
LoadingState<List<Package>?> loadingState,
|
||||
) {
|
||||
late final color = Get.currentRoute.startsWith('/whisperDetail')
|
||||
? theme.colorScheme.surface
|
||||
: theme.colorScheme.onInverseSurface;
|
||||
late final color = ElevationOverlay.colorWithOverlay(
|
||||
theme.colorScheme.surface,
|
||||
theme.hoverColor,
|
||||
Get.currentRoute.startsWith('/whisperDetail') ? 8 : 2,
|
||||
);
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success(:var response) =>
|
||||
|
||||
@@ -74,7 +74,7 @@ class EpisodePanel extends CommonSlidePage {
|
||||
final int initialTabIndex;
|
||||
final bool? isSupportReverse;
|
||||
final bool? isReversed;
|
||||
final ValueChanged<ugc.BaseEpisodeItem> onChangeEpisode;
|
||||
final Future<bool> Function(ugc.BaseEpisodeItem) onChangeEpisode;
|
||||
final VoidCallback? onReverse;
|
||||
final VoidCallback? onClose;
|
||||
|
||||
@@ -432,19 +432,23 @@ class _EpisodePanelState extends State<EpisodePanel>
|
||||
}
|
||||
SmartDialog.showToast('切换到:$title');
|
||||
widget.onClose?.call();
|
||||
if (!showTitle) {
|
||||
_currentItemIndex = index;
|
||||
}
|
||||
widget.onChangeEpisode(episode);
|
||||
if (widget.type == EpisodeType.season) {
|
||||
try {
|
||||
Get.find<VideoDetailController>(
|
||||
tag: widget.ugcIntroController!.heroTag,
|
||||
).seasonCid = episode.cid;
|
||||
} catch (_) {
|
||||
if (kDebugMode) rethrow;
|
||||
|
||||
widget.onChangeEpisode(episode).then((res) {
|
||||
if (res) {
|
||||
if (!showTitle) {
|
||||
_currentItemIndex = index;
|
||||
}
|
||||
if (widget.type == EpisodeType.season) {
|
||||
try {
|
||||
Get.find<VideoDetailController>(
|
||||
tag: widget.ugcIntroController!.heroTag,
|
||||
).seasonCid = episode.cid;
|
||||
} catch (_) {
|
||||
if (kDebugMode) rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
onLongPress: () {
|
||||
if (cover?.isNotEmpty == true) {
|
||||
|
||||
@@ -259,57 +259,58 @@ class _CreateFavPageState extends State<CreateFavPage> {
|
||||
],
|
||||
ListTile(
|
||||
tileColor: theme.colorScheme.onInverseSurface,
|
||||
leading: Text.rich(
|
||||
style: const TextStyle(
|
||||
height: 1,
|
||||
fontSize: 14,
|
||||
),
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '*',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
height: 1,
|
||||
color: theme.colorScheme.error,
|
||||
title: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 55,
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '*',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: theme.colorScheme.error,
|
||||
),
|
||||
),
|
||||
const TextSpan(
|
||||
text: '名称',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const TextSpan(
|
||||
text: '名称',
|
||||
),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
autofocus: true,
|
||||
readOnly: _attr != null && FavUtils.isDefaultFav(_attr!),
|
||||
controller: _titleController,
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
fontSize: 14,
|
||||
color: _attr != null && FavUtils.isDefaultFav(_attr!)
|
||||
? theme.colorScheme.outline
|
||||
: null,
|
||||
),
|
||||
inputFormatters: [
|
||||
LengthLimitingTextInputFormatter(20),
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
hintText: '名称',
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 14,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
border: const OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
gapPadding: 0,
|
||||
),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
title: TextField(
|
||||
autofocus: true,
|
||||
readOnly: _attr != null && FavUtils.isDefaultFav(_attr!),
|
||||
controller: _titleController,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: _attr != null && FavUtils.isDefaultFav(_attr!)
|
||||
? theme.colorScheme.outline
|
||||
: null,
|
||||
),
|
||||
inputFormatters: [
|
||||
LengthLimitingTextInputFormatter(20),
|
||||
),
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
hintText: '名称',
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 14,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
border: const OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
gapPadding: 0,
|
||||
),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
@@ -319,24 +320,16 @@ class _CreateFavPageState extends State<CreateFavPage> {
|
||||
title: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '简介',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const TextSpan(
|
||||
text: '*',
|
||||
style: TextStyle(color: Colors.transparent),
|
||||
),
|
||||
],
|
||||
SizedBox(
|
||||
width: 55,
|
||||
child: Text(
|
||||
'简介',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
minLines: 6,
|
||||
|
||||
@@ -254,20 +254,25 @@ class _HistoryPageState extends State<HistoryPage>
|
||||
padding: const EdgeInsets.only(left: 16, right: 6),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.info_outline,
|
||||
size: 18,
|
||||
color: theme.onSecondaryContainer,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'历史记录功能已关闭',
|
||||
child: Text.rich(
|
||||
strutStyle: const StrutStyle(height: 1, leading: 0),
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
color: theme.onSecondaryContainer,
|
||||
),
|
||||
TextSpan(
|
||||
children: [
|
||||
WidgetSpan(
|
||||
child: Icon(
|
||||
Icons.info_outline,
|
||||
size: 18,
|
||||
color: theme.onSecondaryContainer,
|
||||
),
|
||||
),
|
||||
const TextSpan(text: ' 历史记录功能已关闭'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
|
||||
@@ -45,7 +45,11 @@ class _LiveEmotePanelState extends State<LiveEmotePanel>
|
||||
|
||||
Widget _buildBody(LoadingState<List<LiveEmoteDatum>?> loadingState) {
|
||||
late final theme = Theme.of(context);
|
||||
late final color = theme.colorScheme.onInverseSurface;
|
||||
late final color = ElevationOverlay.colorWithOverlay(
|
||||
theme.colorScheme.surface,
|
||||
theme.hoverColor,
|
||||
2,
|
||||
);
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success(:var response) =>
|
||||
|
||||
@@ -140,7 +140,7 @@ class LiveRoomChatPanel extends StatelessWidget {
|
||||
),
|
||||
padding: const EdgeInsets.fromLTRB(10, 4, 4, 4),
|
||||
child: Text.rich(
|
||||
style: const TextStyle(color: Colors.white),
|
||||
style: const TextStyle(color: Colors.white, height: 1),
|
||||
strutStyle: const StrutStyle(height: 1, leading: 0),
|
||||
TextSpan(
|
||||
children: [
|
||||
|
||||
@@ -51,8 +51,12 @@ class _MainAppState extends State<MainApp>
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
final brightness = Theme.brightnessOf(context);
|
||||
NetworkImgLayer.reduce =
|
||||
NetworkImgLayer.reduceLuxColor != null && context.isDarkMode;
|
||||
NetworkImgLayer.reduceLuxColor != null && brightness.isDark;
|
||||
if (Utils.isDesktop) {
|
||||
windowManager.setBrightness(brightness);
|
||||
}
|
||||
PageUtils.routeObserver.subscribe(
|
||||
this,
|
||||
ModalRoute.of(context) as PageRoute,
|
||||
@@ -160,6 +164,8 @@ class _MainAppState extends State<MainApp>
|
||||
Future<void> _handleTray() async {
|
||||
if (Platform.isWindows) {
|
||||
await trayManager.setIcon('assets/images/logo/app_icon.ico');
|
||||
} else {
|
||||
await trayManager.setIcon('assets/images/logo/logo_large.png');
|
||||
}
|
||||
if (!Platform.isLinux) {
|
||||
await trayManager.setToolTip(Constants.appName);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:PiliPlus/models_new/video/video_ai_conclusion/model_result.dart';
|
||||
import 'package:PiliPlus/pages/common/slide/common_slide_page.dart';
|
||||
import 'package:PiliPlus/pages/video/controller.dart';
|
||||
import 'package:PiliPlus/pages/video/introduction/ugc/widgets/selectable_text.dart';
|
||||
import 'package:PiliPlus/utils/duration_utils.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -69,7 +70,7 @@ class _AiDetailState extends State<AiConclusionPanel>
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14),
|
||||
child: SelectableText(
|
||||
child: selectableText(
|
||||
widget.item.summary!,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
@@ -98,57 +99,59 @@ class _AiDetailState extends State<AiConclusionPanel>
|
||||
itemCount: widget.item.outline!.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = widget.item.outline![index];
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (index != 0) const SizedBox(height: 10),
|
||||
SelectableText(
|
||||
item.title!,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
height: 1.5,
|
||||
return SelectionArea(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (index != 0) const SizedBox(height: 10),
|
||||
Text(
|
||||
item.title!,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
...?item.partOutline?.map(
|
||||
(item) => Wrap(
|
||||
children: [
|
||||
SelectableText.rich(
|
||||
TextSpan(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: theme.colorScheme.onSurface,
|
||||
height: 1.5,
|
||||
),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: DurationUtils.formatDuration(
|
||||
item.timestamp,
|
||||
),
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
try {
|
||||
Get.find<VideoDetailController>(
|
||||
tag: Get.arguments['heroTag'],
|
||||
).plPlayerController.seekTo(
|
||||
Duration(seconds: item.timestamp!),
|
||||
);
|
||||
} catch (_) {}
|
||||
},
|
||||
const SizedBox(height: 6),
|
||||
...?item.partOutline?.map(
|
||||
(item) => Wrap(
|
||||
children: [
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: theme.colorScheme.onSurface,
|
||||
height: 1.5,
|
||||
),
|
||||
const TextSpan(text: ' '),
|
||||
TextSpan(text: item.content!),
|
||||
],
|
||||
children: [
|
||||
TextSpan(
|
||||
text: DurationUtils.formatDuration(
|
||||
item.timestamp,
|
||||
),
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
try {
|
||||
Get.find<VideoDetailController>(
|
||||
tag: Get.arguments['heroTag'],
|
||||
).plPlayerController.seekTo(
|
||||
Duration(seconds: item.timestamp!),
|
||||
);
|
||||
} catch (_) {}
|
||||
},
|
||||
),
|
||||
const TextSpan(text: ' '),
|
||||
TextSpan(text: item.content!),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -277,7 +277,7 @@ class PgcIntroController extends CommonIntroController {
|
||||
}
|
||||
|
||||
// 修改分P或番剧分集
|
||||
Future<void> onChangeEpisode(BaseEpisodeItem episode) async {
|
||||
Future<bool> onChangeEpisode(BaseEpisodeItem episode) async {
|
||||
try {
|
||||
final int epId = episode.epId ?? episode.id!;
|
||||
final String bvid = episode.bvid ?? this.bvid;
|
||||
@@ -285,7 +285,7 @@ class PgcIntroController extends CommonIntroController {
|
||||
final int? cid =
|
||||
episode.cid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid);
|
||||
if (cid == null) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
final String? cover = episode.cover;
|
||||
|
||||
@@ -323,8 +323,10 @@ class PgcIntroController extends CommonIntroController {
|
||||
this.cid.value = cid;
|
||||
queryOnlineTotal();
|
||||
queryVideoIntro(episode as EpisodeItem);
|
||||
return true;
|
||||
} catch (e) {
|
||||
if (kDebugMode) debugPrint('pgc onChangeEpisode: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:PiliPlus/models_new/video/video_tag/data.dart';
|
||||
import 'package:PiliPlus/pages/common/slide/common_slide_page.dart';
|
||||
import 'package:PiliPlus/pages/pgc_review/view.dart';
|
||||
import 'package:PiliPlus/pages/search/widgets/search_text.dart';
|
||||
import 'package:PiliPlus/pages/video/introduction/ugc/widgets/selectable_text.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart' hide TabBarView;
|
||||
@@ -108,7 +109,7 @@ class _IntroDetailState extends State<PgcIntroPanel>
|
||||
bottom: MediaQuery.viewPaddingOf(context).bottom + 100,
|
||||
),
|
||||
children: [
|
||||
SelectableText(
|
||||
selectableText(
|
||||
widget.item.title!,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
@@ -152,7 +153,7 @@ class _IntroDetailState extends State<PgcIntroPanel>
|
||||
style: theme.textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
SelectableText(
|
||||
selectableText(
|
||||
widget.item.evaluate!,
|
||||
style: textStyle,
|
||||
),
|
||||
|
||||
@@ -463,7 +463,7 @@ class UgcIntroController extends CommonIntroController with ReloadMixin {
|
||||
}
|
||||
|
||||
// 修改分P或番剧分集
|
||||
Future<void> onChangeEpisode(
|
||||
Future<bool> onChangeEpisode(
|
||||
BaseEpisodeItem episode, {
|
||||
bool isStein = false,
|
||||
}) async {
|
||||
@@ -473,7 +473,7 @@ class UgcIntroController extends CommonIntroController with ReloadMixin {
|
||||
final int? cid =
|
||||
episode.cid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid);
|
||||
if (cid == null) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
final String? cover = episode.cover;
|
||||
|
||||
@@ -488,7 +488,7 @@ class UgcIntroController extends CommonIntroController with ReloadMixin {
|
||||
cid: cid,
|
||||
cover: cover,
|
||||
);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,8 +546,10 @@ class UgcIntroController extends CommonIntroController with ReloadMixin {
|
||||
|
||||
this.cid.value = cid;
|
||||
queryOnlineTotal();
|
||||
return true;
|
||||
} catch (e) {
|
||||
if (kDebugMode) debugPrint('ugc onChangeEpisode: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import 'package:PiliPlus/pages/video/introduction/ugc/controller.dart';
|
||||
import 'package:PiliPlus/pages/video/introduction/ugc/widgets/action_item.dart';
|
||||
import 'package:PiliPlus/pages/video/introduction/ugc/widgets/page.dart';
|
||||
import 'package:PiliPlus/pages/video/introduction/ugc/widgets/season.dart';
|
||||
import 'package:PiliPlus/pages/video/introduction/ugc/widgets/selectable_text.dart';
|
||||
import 'package:PiliPlus/pages/video/introduction/ugc/widgets/triple_state.dart';
|
||||
import 'package:PiliPlus/utils/app_scheme.dart';
|
||||
import 'package:PiliPlus/utils/date_utils.dart';
|
||||
@@ -327,13 +328,9 @@ class _UgcIntroPanelState extends TripleState<UgcIntroPanel> {
|
||||
),
|
||||
if (videoDetail.descV2?.isNotEmpty == true) ...[
|
||||
const SizedBox(height: 8),
|
||||
SelectableText.rich(
|
||||
selectableRichText(
|
||||
style: const TextStyle(height: 1.4),
|
||||
TextSpan(
|
||||
children: [
|
||||
buildContent(theme, videoDetail),
|
||||
],
|
||||
),
|
||||
buildContent(theme, videoDetail),
|
||||
),
|
||||
],
|
||||
Obx(() {
|
||||
@@ -601,7 +598,7 @@ class _UgcIntroPanelState extends TripleState<UgcIntroPanel> {
|
||||
caseSensitive: false,
|
||||
);
|
||||
|
||||
InlineSpan buildContent(ThemeData theme, VideoDetailData content) {
|
||||
TextSpan buildContent(ThemeData theme, VideoDetailData content) {
|
||||
if (content.descV2.isNullOrEmpty) {
|
||||
return const TextSpan();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
Widget selectableText(
|
||||
String text, {
|
||||
TextStyle? style,
|
||||
}) {
|
||||
if (Utils.isDesktop) {
|
||||
return SelectionArea(
|
||||
child: Text(
|
||||
style: style,
|
||||
text,
|
||||
),
|
||||
);
|
||||
}
|
||||
return SelectableText(
|
||||
style: style,
|
||||
text,
|
||||
);
|
||||
}
|
||||
|
||||
Widget selectableRichText(
|
||||
TextSpan textSpan, {
|
||||
TextStyle? style,
|
||||
}) {
|
||||
if (Utils.isDesktop) {
|
||||
return SelectionArea(
|
||||
child: Text.rich(
|
||||
style: style,
|
||||
textSpan,
|
||||
),
|
||||
);
|
||||
}
|
||||
return SelectableText.rich(
|
||||
style: style,
|
||||
textSpan,
|
||||
);
|
||||
}
|
||||
@@ -44,6 +44,11 @@ class _WhisperDetailPageState
|
||||
Widget build(BuildContext context) {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
final padding = MediaQuery.viewPaddingOf(context);
|
||||
late final containerColor = ElevationOverlay.colorWithOverlay(
|
||||
theme.colorScheme.surface,
|
||||
theme.hoverColor,
|
||||
1,
|
||||
);
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
appBar: AppBar(
|
||||
@@ -145,8 +150,11 @@ class _WhisperDetailPageState
|
||||
),
|
||||
),
|
||||
if (_whisperDetailController.mid != null) ...[
|
||||
_buildInputView(theme),
|
||||
buildPanelContainer(theme, theme.colorScheme.onInverseSurface),
|
||||
_buildInputView(theme, containerColor),
|
||||
buildPanelContainer(
|
||||
theme,
|
||||
containerColor,
|
||||
),
|
||||
] else
|
||||
SizedBox(height: padding.bottom),
|
||||
],
|
||||
@@ -228,11 +236,11 @@ class _WhisperDetailPageState
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInputView(ThemeData theme) {
|
||||
Widget _buildInputView(ThemeData theme, Color containerColor) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.onInverseSurface,
|
||||
color: containerColor,
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(16)),
|
||||
),
|
||||
child: Row(
|
||||
|
||||
Reference in New Issue
Block a user