design updates + bug fixes
This commit is contained in:
parent
9b221c4463
commit
1d401b3ba0
|
|
@ -3,7 +3,7 @@ import 'package:didvan/utils/action_sheet.dart';
|
|||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class CoreProvier with ChangeNotifier {
|
||||
AppState _appState = AppState.idle;
|
||||
AppState _appState = AppState.busy;
|
||||
|
||||
set appState(AppState newState) {
|
||||
if (newState == AppState.isolatedBusy) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:didvan/services/network/request.dart';
|
||||
import 'package:didvan/services/network/request_helper.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
class ServerDataProvider {
|
||||
static final List<MapEntry> directTypes = [];
|
||||
|
|
@ -10,7 +11,10 @@ class ServerDataProvider {
|
|||
|
||||
static int labelToTypeId(String label) => label.contains('پشتیبانی')
|
||||
? 7
|
||||
: directTypes.firstWhere((element) => element.value.contains(label)).key;
|
||||
: directTypes
|
||||
.firstWhereOrNull((element) => element.value.contains(label))
|
||||
?.key ??
|
||||
7;
|
||||
|
||||
static Future<void> _getDirectTypes() async {
|
||||
final service = RequestService(RequestHelper.directTypes);
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@ class UserProvider extends CoreProvier {
|
|||
isAuthenticated = true;
|
||||
final RequestService service = RequestService(RequestHelper.userInfo);
|
||||
await service.httpGet();
|
||||
if (service.statusCode == 401 || service.result['user'] == null) {
|
||||
if (service.statusCode == 401 ||
|
||||
(service.isSuccess && service.result['user'] == null)) {
|
||||
return false;
|
||||
}
|
||||
if (service.isSuccess) {
|
||||
|
|
|
|||
|
|
@ -180,18 +180,24 @@ class RouteGenerator {
|
|||
final shortestSide = MediaQuery.of(context).size.shortestSide;
|
||||
final bool useMobileLayout = shortestSide < 600;
|
||||
if (kIsWeb && !useMobileLayout) {
|
||||
return Container(
|
||||
return MediaQuery(
|
||||
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
|
||||
child: Container(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
alignment: Alignment.center,
|
||||
child: AspectRatio(aspectRatio: 9 / 16, child: page),
|
||||
),
|
||||
);
|
||||
}
|
||||
return Container(
|
||||
return MediaQuery(
|
||||
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
|
||||
child: Container(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: SafeArea(
|
||||
child: page,
|
||||
top: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ class CommentsState extends CoreProvier {
|
|||
int itemId = 0;
|
||||
|
||||
Future<void> getComments() async {
|
||||
appState = AppState.busy;
|
||||
final service = RequestService(
|
||||
RequestHelper.comments(itemId, type),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ class _NewsDetailsState extends State<NewsDetails> {
|
|||
state: state,
|
||||
builder: (context, state) => Stack(
|
||||
children: [
|
||||
if (state.news.isNotEmpty)
|
||||
IgnorePointer(
|
||||
ignoring: state.isFetchingNewItem,
|
||||
child: DidvanPageView(
|
||||
|
|
@ -55,7 +54,6 @@ class _NewsDetailsState extends State<NewsDetails> {
|
|||
currentIndex: state.currentIndex,
|
||||
),
|
||||
),
|
||||
if (state.news.isNotEmpty)
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ class _RadarDetailsState extends State<RadarDetails> {
|
|||
state: state,
|
||||
builder: (context, state) => Stack(
|
||||
children: [
|
||||
if (state.radars.isNotEmpty)
|
||||
IgnorePointer(
|
||||
ignoring: state.isFetchingNewItem,
|
||||
child: DidvanPageView(
|
||||
|
|
@ -55,7 +54,6 @@ class _RadarDetailsState extends State<RadarDetails> {
|
|||
currentIndex: state.currentIndex,
|
||||
),
|
||||
),
|
||||
if (state.radars.isNotEmpty)
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ import 'package:didvan/services/network/request.dart';
|
|||
import 'package:didvan/services/network/request_helper.dart';
|
||||
|
||||
class FilteredBookmarksState extends CoreProvier {
|
||||
String search = '';
|
||||
String lastSearch = '';
|
||||
final List<OverviewData> bookmarks = [];
|
||||
final String type;
|
||||
int page = 1;
|
||||
|
|
@ -15,17 +13,8 @@ class FilteredBookmarksState extends CoreProvier {
|
|||
|
||||
FilteredBookmarksState(this.type);
|
||||
|
||||
bool get searching => search != '';
|
||||
|
||||
Future<void> getBookmarks({required int page}) async {
|
||||
if (search != '') {
|
||||
lastSearch = search;
|
||||
}
|
||||
if (page == 1) {
|
||||
bookmarks.clear();
|
||||
}
|
||||
this.page = page;
|
||||
appState = AppState.busy;
|
||||
String typeString = '';
|
||||
if (type == 'video' || type == 'podcast') {
|
||||
typeString = 'studios';
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ class DirectListState extends CoreProvier {
|
|||
}
|
||||
|
||||
Future<void> getDirectsList() async {
|
||||
appState = AppState.busy;
|
||||
final RequestService service = RequestService(RequestHelper.directs);
|
||||
await service.httpGet();
|
||||
if (service.isSuccess) {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import 'package:didvan/config/design_config.dart';
|
|||
import 'package:didvan/config/theme_data.dart';
|
||||
import 'package:didvan/models/view/app_bar_data.dart';
|
||||
import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart';
|
||||
import 'package:didvan/views/home/studio/studio_details/widgets/details_tab_bar.dart';
|
||||
import 'package:didvan/views/home/studio/studio_details/widgets/studio_details_widget.dart';
|
||||
import 'package:didvan/views/home/widgets/bookmark_button.dart';
|
||||
import 'package:didvan/views/widgets/didvan/scaffold.dart';
|
||||
import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
|
@ -39,7 +41,7 @@ class _StudioDetailsState extends State<StudioDetails> {
|
|||
() => state.getStudioDetails(widget.pageData['id']),
|
||||
);
|
||||
state.args = widget.pageData['args'];
|
||||
if (!kIsWeb && Platform.isAndroid) WebView.platform = AndroidWebView();
|
||||
if (Platform.isAndroid) WebView.platform = AndroidWebView();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
|
@ -107,9 +109,27 @@ class _StudioDetailsState extends State<StudioDetails> {
|
|||
appBarData: _isFullScreen
|
||||
? null
|
||||
: AppBarData(
|
||||
trailing: BookmarkButton(
|
||||
value: state.studio.marked,
|
||||
onMarkChanged: (value) => widget
|
||||
.pageData['onMarkChanged'](state.studio.id, value),
|
||||
gestureSize: 48,
|
||||
),
|
||||
isSmall: true,
|
||||
title: state.studio.title,
|
||||
),
|
||||
showSliversFirst: true,
|
||||
slivers: [
|
||||
SliverAppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
pinned: true,
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
toolbarHeight:
|
||||
(_isFullScreen ? ds.height : ds.width * 9 / 16) +
|
||||
72 -
|
||||
MediaQuery.of(context).padding.top,
|
||||
elevation: 0,
|
||||
flexibleSpace: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: ds.width,
|
||||
|
|
@ -117,7 +137,8 @@ class _StudioDetailsState extends State<StudioDetails> {
|
|||
child: Stack(
|
||||
children: [
|
||||
WebView(
|
||||
backgroundColor: Theme.of(context).colorScheme.black,
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.black,
|
||||
allowsInlineMediaPlayback: true,
|
||||
initialUrl: Uri.dataFromString(
|
||||
'''
|
||||
|
|
@ -172,9 +193,8 @@ class _StudioDetailsState extends State<StudioDetails> {
|
|||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
StudioDetailsWidget(
|
||||
scrollController: _scrollController,
|
||||
DetailsTabBar(
|
||||
isVideo: true,
|
||||
onCommentsTabSelected: () => Future.delayed(
|
||||
const Duration(milliseconds: 100),
|
||||
() => _scrollController.animateTo(
|
||||
|
|
@ -183,6 +203,14 @@ class _StudioDetailsState extends State<StudioDetails> {
|
|||
curve: Curves.easeIn,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
children: [
|
||||
StudioDetailsWidget(
|
||||
scrollController: _scrollController,
|
||||
studio: state.studio,
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -2,18 +2,17 @@ import 'dart:io';
|
|||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:didvan/config/design_config.dart';
|
||||
import 'package:didvan/config/theme_data.dart';
|
||||
import 'package:didvan/models/view/app_bar_data.dart';
|
||||
import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart';
|
||||
import 'package:didvan/views/home/studio/studio_details/widgets/details_tab_bar.dart';
|
||||
import 'package:didvan/views/home/studio/studio_details/widgets/studio_details_widget.dart';
|
||||
import 'package:didvan/views/home/widgets/bookmark_button.dart';
|
||||
import 'package:didvan/views/widgets/didvan/scaffold.dart';
|
||||
import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:universal_html/html.dart' as html;
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
class StudioDetails extends StatefulWidget {
|
||||
final Map<String, dynamic> pageData;
|
||||
|
|
@ -28,10 +27,6 @@ class _StudioDetailsState extends State<StudioDetails> {
|
|||
final _scrollController = ScrollController();
|
||||
|
||||
bool _isFullScreen = false;
|
||||
bool _isInit = true;
|
||||
|
||||
double _dwInPortrait = 0;
|
||||
double _scaleInPortrait = 1;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -41,7 +36,6 @@ class _StudioDetailsState extends State<StudioDetails> {
|
|||
() => state.getStudioDetails(widget.pageData['id']),
|
||||
);
|
||||
state.args = widget.pageData['args'];
|
||||
if (!kIsWeb && Platform.isAndroid) WebView.platform = AndroidWebView();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
|
@ -77,12 +71,6 @@ class _StudioDetailsState extends State<StudioDetails> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ds = MediaQuery.of(context).size;
|
||||
if (_isInit) {
|
||||
_dwInPortrait = MediaQuery.of(context).size.width;
|
||||
_scaleInPortrait = _dwInPortrait / 576;
|
||||
_isInit = false;
|
||||
}
|
||||
|
||||
return Consumer<StudioDetailsState>(
|
||||
builder: (context, state, child) => StateHandler<StudioDetailsState>(
|
||||
state: state,
|
||||
|
|
@ -121,79 +109,31 @@ class _StudioDetailsState extends State<StudioDetails> {
|
|||
: AppBarData(
|
||||
isSmall: true,
|
||||
title: state.studio.title,
|
||||
trailing: BookmarkButton(
|
||||
value: state.studio.marked,
|
||||
onMarkChanged: (value) => widget
|
||||
.pageData['onMarkChanged'](state.studio.id, value),
|
||||
gestureSize: 48,
|
||||
),
|
||||
),
|
||||
showSliversFirst: true,
|
||||
slivers: [
|
||||
SliverAppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
pinned: true,
|
||||
elevation: 0,
|
||||
toolbarHeight:
|
||||
(_isFullScreen ? ds.height : ds.width * 9 / 16) +
|
||||
72 -
|
||||
MediaQuery.of(context).padding.top,
|
||||
flexibleSpace: Column(
|
||||
children: [
|
||||
if (kIsWeb)
|
||||
const AspectRatio(
|
||||
aspectRatio: 16 / 9,
|
||||
child: HtmlElementView(viewType: 'video'),
|
||||
),
|
||||
if (!kIsWeb)
|
||||
SizedBox(
|
||||
width: ds.width,
|
||||
height: _isFullScreen ? ds.height : ds.width * 9 / 16,
|
||||
child: Stack(
|
||||
children: [
|
||||
WebView(
|
||||
backgroundColor: Theme.of(context).colorScheme.black,
|
||||
allowsInlineMediaPlayback: true,
|
||||
initialUrl: Uri.dataFromString(
|
||||
'''
|
||||
<html>
|
||||
<head>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=$_scaleInPortrait"
|
||||
/>
|
||||
<style>
|
||||
* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
iframe {
|
||||
max-height: 100vh;
|
||||
}
|
||||
.r1_iframe_embed {
|
||||
height: ${MediaQuery.of(context).size.width / _scaleInPortrait}px !important;
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
@media(max-width:580px){
|
||||
.r1_iframe_embed {
|
||||
height: ${_dwInPortrait * 9 / 16 / _scaleInPortrait}px !important;
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
${state.studio.media}
|
||||
</body>
|
||||
</html>
|
||||
''',
|
||||
mimeType: 'text/html',
|
||||
).toString(),
|
||||
javascriptMode: JavascriptMode.unrestricted,
|
||||
),
|
||||
if (!kIsWeb)
|
||||
Positioned(
|
||||
right: 42,
|
||||
bottom: 8,
|
||||
child: GestureDetector(
|
||||
onTap: () => _changeFullSceen(!_isFullScreen),
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
width: 24,
|
||||
height: 30,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
StudioDetailsWidget(
|
||||
scrollController: _scrollController,
|
||||
DetailsTabBar(
|
||||
isVideo: true,
|
||||
onCommentsTabSelected: () => Future.delayed(
|
||||
const Duration(milliseconds: 100),
|
||||
() => _scrollController.animateTo(
|
||||
|
|
@ -202,6 +142,14 @@ class _StudioDetailsState extends State<StudioDetails> {
|
|||
curve: Curves.easeIn,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
children: [
|
||||
StudioDetailsWidget(
|
||||
scrollController: _scrollController,
|
||||
studio: state.studio,
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ class StudioDetailsState extends CoreProvier {
|
|||
final service = RequestService(RequestHelper.tag(
|
||||
ids: studio.tags.map((tag) => tag.id).toList(),
|
||||
itemId: studio.id,
|
||||
type: studio.media.contains('iframe') ? 'video' : 'podcast',
|
||||
type: args.type,
|
||||
));
|
||||
await service.httpGet();
|
||||
if (service.isSuccess) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,134 @@
|
|||
import 'package:didvan/config/design_config.dart';
|
||||
import 'package:didvan/config/theme_data.dart';
|
||||
import 'package:didvan/constants/app_icons.dart';
|
||||
import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart';
|
||||
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class DetailsTabBar extends StatelessWidget {
|
||||
final bool isVideo;
|
||||
final VoidCallback onCommentsTabSelected;
|
||||
|
||||
const DetailsTabBar({
|
||||
Key? key,
|
||||
required this.isVideo,
|
||||
required this.onCommentsTabSelected,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final state = context.read<StudioDetailsState>();
|
||||
return WillPopScope(
|
||||
onWillPop: () async {
|
||||
state.selectedDetailsIndex = 0;
|
||||
return true;
|
||||
},
|
||||
child: Container(
|
||||
height: 72,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: const Color(0XFF1B3C59).withOpacity(0.15),
|
||||
offset: const Offset(0, 8),
|
||||
blurRadius: 8,
|
||||
spreadRadius: 0,
|
||||
)
|
||||
],
|
||||
),
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: Row(
|
||||
children: [
|
||||
_TabItem(
|
||||
icon: DidvanIcons.description_solid,
|
||||
title: 'توضیحات',
|
||||
onTap: () => state.selectedDetailsIndex = 0,
|
||||
isSelected: state.selectedDetailsIndex == 0,
|
||||
isVideo: isVideo,
|
||||
),
|
||||
_TabItem(
|
||||
icon: DidvanIcons.chats_solid,
|
||||
title: 'نظرات',
|
||||
onTap: () {
|
||||
state.selectedDetailsIndex = 1;
|
||||
onCommentsTabSelected();
|
||||
},
|
||||
isSelected: state.selectedDetailsIndex == 1,
|
||||
isVideo: isVideo,
|
||||
),
|
||||
_TabItem(
|
||||
icon: DidvanIcons.puzzle_solid,
|
||||
title: 'مطالب مرتبط',
|
||||
onTap: () => state.selectedDetailsIndex = 2,
|
||||
isSelected: state.selectedDetailsIndex == 2,
|
||||
isVideo: isVideo,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TabItem extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String title;
|
||||
final VoidCallback onTap;
|
||||
final bool isSelected;
|
||||
final bool isVideo;
|
||||
const _TabItem({
|
||||
Key? key,
|
||||
required this.icon,
|
||||
required this.title,
|
||||
required this.onTap,
|
||||
required this.isSelected,
|
||||
required this.isVideo,
|
||||
}) : super(key: key);
|
||||
|
||||
Color? _color(context) {
|
||||
if (isSelected) {
|
||||
if (isVideo) {
|
||||
return Theme.of(context).colorScheme.secondary;
|
||||
}
|
||||
return Theme.of(context).colorScheme.focusedBorder;
|
||||
}
|
||||
return Theme.of(context).colorScheme.border;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Column(
|
||||
children: [
|
||||
Icon(
|
||||
icon,
|
||||
color: _color(context),
|
||||
),
|
||||
AnimatedContainer(
|
||||
duration: DesignConfig.lowAnimationDuration,
|
||||
width: isSelected ? 64 : 0,
|
||||
height: 1,
|
||||
color: _color(context),
|
||||
),
|
||||
DidvanText(
|
||||
title,
|
||||
color: _color(context),
|
||||
style: Theme.of(context).textTheme.caption,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import 'package:didvan/models/studio_details_data.dart';
|
|||
import 'package:didvan/views/home/comments/comments.dart';
|
||||
import 'package:didvan/views/home/comments/comments_state.dart';
|
||||
import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart';
|
||||
import 'package:didvan/views/home/studio/studio_details/widgets/details_tab_bar.dart';
|
||||
import 'package:didvan/views/home/widgets/overview/multitype.dart';
|
||||
import 'package:didvan/views/home/widgets/tag_item.dart';
|
||||
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||
|
|
@ -16,15 +17,15 @@ import 'package:provider/provider.dart';
|
|||
class StudioDetailsWidget extends StatelessWidget {
|
||||
final StudioDetailsData studio;
|
||||
final ScrollController? scrollController;
|
||||
final VoidCallback onCommentsTabSelected;
|
||||
final VoidCallback? onCommentsTabSelected;
|
||||
const StudioDetailsWidget({
|
||||
Key? key,
|
||||
required this.studio,
|
||||
required this.onCommentsTabSelected,
|
||||
this.onCommentsTabSelected,
|
||||
this.scrollController,
|
||||
}) : super(key: key);
|
||||
|
||||
bool get _isVideo => studio.media.contains('ifram');
|
||||
bool get _isVideo => studio.media.contains('iframe');
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -35,38 +36,12 @@ class StudioDetailsWidget extends StatelessWidget {
|
|||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
const SizedBox(),
|
||||
_TabItem(
|
||||
icon: DidvanIcons.description_solid,
|
||||
title: 'توضیحات',
|
||||
onTap: () => state.selectedDetailsIndex = 0,
|
||||
isSelected: state.selectedDetailsIndex == 0,
|
||||
if (!_isVideo)
|
||||
DetailsTabBar(
|
||||
isVideo: _isVideo,
|
||||
onCommentsTabSelected: onCommentsTabSelected ?? () {},
|
||||
),
|
||||
_TabItem(
|
||||
icon: DidvanIcons.chats_solid,
|
||||
title: 'نظرات',
|
||||
onTap: () {
|
||||
state.selectedDetailsIndex = 1;
|
||||
onCommentsTabSelected();
|
||||
},
|
||||
isSelected: state.selectedDetailsIndex == 1,
|
||||
isVideo: _isVideo,
|
||||
),
|
||||
_TabItem(
|
||||
icon: DidvanIcons.puzzle_solid,
|
||||
title: 'مطالب مرتبط',
|
||||
onTap: () => state.selectedDetailsIndex = 2,
|
||||
isSelected: state.selectedDetailsIndex == 2,
|
||||
isVideo: _isVideo,
|
||||
),
|
||||
const SizedBox(),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
if (state.selectedDetailsIndex != 1) const SizedBox(height: 16),
|
||||
StateHandler<StudioDetailsState>(
|
||||
onRetry: () {},
|
||||
state: state,
|
||||
|
|
@ -116,7 +91,10 @@ class StudioDetailsWidget extends StatelessWidget {
|
|||
return ChangeNotifierProvider<CommentsState>(
|
||||
create: (context) => CommentsState(),
|
||||
child: SizedBox(
|
||||
height: ds.height - 180,
|
||||
height: ds.height -
|
||||
ds.width * 9 / 16 -
|
||||
128 -
|
||||
MediaQuery.of(context).padding.top,
|
||||
child: Comments(
|
||||
pageData: {
|
||||
'id': studio.id,
|
||||
|
|
@ -164,63 +142,6 @@ class StudioDetailsWidget extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
class _TabItem extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String title;
|
||||
final VoidCallback onTap;
|
||||
final bool isSelected;
|
||||
final bool isVideo;
|
||||
const _TabItem({
|
||||
Key? key,
|
||||
required this.icon,
|
||||
required this.title,
|
||||
required this.onTap,
|
||||
required this.isSelected,
|
||||
required this.isVideo,
|
||||
}) : super(key: key);
|
||||
|
||||
Color? _color(context) {
|
||||
if (isSelected) {
|
||||
if (isVideo) {
|
||||
return Theme.of(context).colorScheme.secondary;
|
||||
}
|
||||
return Theme.of(context).colorScheme.focusedBorder;
|
||||
}
|
||||
return Theme.of(context).colorScheme.border;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Column(
|
||||
children: [
|
||||
Icon(
|
||||
icon,
|
||||
color: _color(context),
|
||||
),
|
||||
if (isSelected) const SizedBox(height: 8),
|
||||
if (isSelected)
|
||||
Container(
|
||||
width: 64,
|
||||
height: 1,
|
||||
color: _color(context),
|
||||
),
|
||||
if (isSelected)
|
||||
DidvanText(
|
||||
title,
|
||||
color: _color(context),
|
||||
style: Theme.of(context).textTheme.caption,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _StudioPreview extends StatelessWidget {
|
||||
final bool isNext;
|
||||
final StudioDetailsData studio;
|
||||
|
|
@ -279,7 +200,7 @@ class _StudioPreview extends StatelessWidget {
|
|||
const SizedBox(height: 8),
|
||||
DidvanText(
|
||||
studio.title,
|
||||
maxLines: 3,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.overline,
|
||||
|
|
|
|||
|
|
@ -114,7 +114,10 @@ class _StudioSliderState extends State<StudioSlider> {
|
|||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
for (var i = 0; i < state.sliders.length; i++)
|
||||
_SliderIndicator(isCurrentIndex: selectedIndex == i),
|
||||
_SliderIndicator(
|
||||
isCurrentIndex: selectedIndex == i,
|
||||
isVideo: state.videosSelected,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
|
@ -125,8 +128,19 @@ class _StudioSliderState extends State<StudioSlider> {
|
|||
|
||||
class _SliderIndicator extends StatelessWidget {
|
||||
final bool isCurrentIndex;
|
||||
const _SliderIndicator({Key? key, required this.isCurrentIndex})
|
||||
: super(key: key);
|
||||
final bool isVideo;
|
||||
const _SliderIndicator({
|
||||
Key? key,
|
||||
required this.isCurrentIndex,
|
||||
required this.isVideo,
|
||||
}) : super(key: key);
|
||||
|
||||
Color _color(BuildContext context) {
|
||||
if (isVideo) {
|
||||
return Theme.of(context).colorScheme.secondary;
|
||||
}
|
||||
return Theme.of(context).colorScheme.focusedBorder;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -137,11 +151,10 @@ class _SliderIndicator extends StatelessWidget {
|
|||
margin: const EdgeInsets.only(left: 4),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.focusedBorder,
|
||||
color: _color(context),
|
||||
),
|
||||
shape: BoxShape.circle,
|
||||
color:
|
||||
isCurrentIndex ? Theme.of(context).colorScheme.focusedBorder : null,
|
||||
color: isCurrentIndex ? _color(context) : null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,7 @@ class StudioTabBar extends StatelessWidget {
|
|||
padding: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: state.videosSelected
|
||||
? Theme.of(context).colorScheme.secondary
|
||||
: Theme.of(context).colorScheme.primary,
|
||||
color: Theme.of(context).colorScheme.border,
|
||||
),
|
||||
borderRadius: DesignConfig.lowBorderRadius,
|
||||
),
|
||||
|
|
@ -88,14 +86,12 @@ class _StudioTypeButton extends StatelessWidget {
|
|||
size: 32,
|
||||
color: _color(context),
|
||||
),
|
||||
if (!isSelected) const SizedBox(height: 18),
|
||||
if (isSelected)
|
||||
Container(
|
||||
width: 88,
|
||||
AnimatedContainer(
|
||||
duration: DesignConfig.lowAnimationDuration,
|
||||
width: isSelected ? 88 : 0,
|
||||
height: 1,
|
||||
color: _color(context),
|
||||
),
|
||||
if (isSelected)
|
||||
DidvanText(
|
||||
title,
|
||||
style: Theme.of(context).textTheme.overline,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:didvan/config/design_config.dart';
|
||||
import 'package:didvan/constants/app_icons.dart';
|
||||
import 'package:didvan/models/view/action_sheet_data.dart';
|
||||
import 'package:didvan/utils/action_sheet.dart';
|
||||
|
|
@ -43,7 +44,10 @@ class _BookmarkButtonState extends State<BookmarkButton> {
|
|||
Widget build(BuildContext context) {
|
||||
return DidvanIconButton(
|
||||
gestureSize: widget.gestureSize,
|
||||
color: widget.color,
|
||||
color: widget.color ??
|
||||
(DesignConfig.isDark || !_value
|
||||
? null
|
||||
: Theme.of(context).colorScheme.primary),
|
||||
icon: _value ? DidvanIcons.bookmark_solid : DidvanIcons.bookmark_regular,
|
||||
onPressed: () async {
|
||||
bool confirm = false;
|
||||
|
|
|
|||
|
|
@ -104,6 +104,9 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
|
|||
const Spacer(),
|
||||
if (widget.isRadar)
|
||||
BookmarkButton(
|
||||
color: DesignConfig.isDark
|
||||
? Theme.of(context).colorScheme.focusedBorder
|
||||
: Theme.of(context).colorScheme.focused,
|
||||
askForConfirmation: widget.hasUnmarkConfirmation,
|
||||
value: widget.item.marked,
|
||||
onMarkChanged: (value) {
|
||||
|
|
@ -143,6 +146,9 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
|
|||
if (!widget.isRadar) const SizedBox(width: 12),
|
||||
if (!widget.isRadar)
|
||||
BookmarkButton(
|
||||
color: DesignConfig.isDark
|
||||
? Theme.of(context).colorScheme.focusedBorder
|
||||
: Theme.of(context).colorScheme.focused,
|
||||
askForConfirmation: widget.hasUnmarkConfirmation,
|
||||
value: widget.item.marked,
|
||||
onMarkChanged: (value) {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import 'package:didvan/models/requests/news.dart';
|
|||
import 'package:didvan/models/requests/radar.dart';
|
||||
import 'package:didvan/models/requests/studio.dart';
|
||||
import 'package:didvan/routes/routes.dart';
|
||||
import 'package:didvan/utils/date_time.dart';
|
||||
import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart';
|
||||
import 'package:didvan/views/widgets/didvan/card.dart';
|
||||
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||
|
|
@ -127,7 +128,22 @@ class MultitypeOverview extends StatelessWidget {
|
|||
DateTime.parse(item.createdAt).toPersianDateStr(),
|
||||
style: Theme.of(context).textTheme.overline,
|
||||
),
|
||||
// DidvanText('text'),
|
||||
const Spacer(),
|
||||
if ((item.timeToRead ?? item.duration) != null) ...[
|
||||
const Icon(
|
||||
DidvanIcons.timer_light,
|
||||
size: 18,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
DidvanText(
|
||||
item.timeToRead != null
|
||||
? 'خواندن در ${item.timeToRead} دقیقه'
|
||||
: DateTimeUtils.normalizeTimeDuration(
|
||||
Duration(seconds: item.duration!),
|
||||
),
|
||||
style: Theme.of(context).textTheme.overline,
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ class DidvanAppBar extends StatelessWidget {
|
|||
return Container(
|
||||
height: appBarData.isSmall ? 56 : 72,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
padding: const EdgeInsets.only(right: 4, left: 20),
|
||||
padding:
|
||||
EdgeInsets.only(right: 4, left: appBarData.trailing == null ? 20 : 0),
|
||||
decoration: BoxDecoration(
|
||||
border: hasBorder
|
||||
? Border(
|
||||
|
|
|
|||
|
|
@ -198,7 +198,13 @@ class DidvanBNB extends StatelessWidget {
|
|||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
),
|
||||
persistentHeader: Column(
|
||||
persistentHeader: GestureDetector(
|
||||
onVerticalDragUpdate: (details) {
|
||||
if (details.delta.dy > 10) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
AudioPlayerWidget(
|
||||
|
|
@ -228,6 +234,7 @@ class DidvanBNB extends StatelessWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
expandableContent: state.appState == AppState.busy
|
||||
? const SizedBox()
|
||||
: StudioDetailsWidget(
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ class DidvanScaffold extends StatefulWidget {
|
|||
final Color? backgroundColor;
|
||||
final bool reverse;
|
||||
final ScrollController? scrollController;
|
||||
final bool showSliversFirst;
|
||||
|
||||
const DidvanScaffold({
|
||||
Key? key,
|
||||
|
|
@ -20,6 +21,7 @@ class DidvanScaffold extends StatefulWidget {
|
|||
this.backgroundColor,
|
||||
this.reverse = false,
|
||||
this.scrollController,
|
||||
this.showSliversFirst = false,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
|
@ -60,7 +62,7 @@ class _DidvanScaffoldState extends State<DidvanScaffold> {
|
|||
pinned: true,
|
||||
flexibleSpace: DidvanAppBar(appBarData: widget.appBarData!),
|
||||
),
|
||||
if (widget.children != null)
|
||||
if (widget.children != null && !widget.showSliversFirst)
|
||||
SliverPadding(
|
||||
padding: widget.padding,
|
||||
sliver: SliverList(
|
||||
|
|
@ -76,6 +78,16 @@ class _DidvanScaffoldState extends State<DidvanScaffold> {
|
|||
padding: widget.padding,
|
||||
sliver: widget.slivers![i],
|
||||
),
|
||||
if (widget.children != null && widget.showSliversFirst)
|
||||
SliverPadding(
|
||||
padding: widget.padding,
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) => widget.children![index],
|
||||
childCount: widget.children!.length,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (widget.reverse)
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
|
|
|
|||
|
|
@ -40,7 +40,9 @@ class SliverStateHandler<T extends CoreProvier> extends SliverList {
|
|||
if (enableEmptyState && state.appState == AppState.idle) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: centerEmptyState ? 120 : 20,
|
||||
top: centerEmptyState
|
||||
? MediaQuery.of(context).size.height / 4
|
||||
: 20,
|
||||
bottom: 20,
|
||||
),
|
||||
child: emptyState,
|
||||
|
|
|
|||
Loading…
Reference in New Issue