new video player basic version
This commit is contained in:
parent
48387b0b79
commit
00f108710a
|
|
@ -1,5 +1,5 @@
|
|||
# Uncomment this line to define a global platform for your project
|
||||
platform :ios, '10.0'
|
||||
platform :ios, '11.0'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
|
|
|
|||
|
|
@ -1,33 +1,40 @@
|
|||
PODS:
|
||||
- audio_session (0.0.1):
|
||||
- Flutter
|
||||
- Firebase/CoreOnly (8.11.0):
|
||||
- FirebaseCore (= 8.11.0)
|
||||
- Firebase/Messaging (8.11.0):
|
||||
- Firebase/CoreOnly
|
||||
- FirebaseMessaging (~> 8.11.0)
|
||||
- firebase_core (1.13.1):
|
||||
- Firebase/CoreOnly (= 8.11.0)
|
||||
- better_player (0.0.1):
|
||||
- Cache (~> 6.0.0)
|
||||
- Flutter
|
||||
- firebase_messaging (11.2.8):
|
||||
- Firebase/Messaging (= 8.11.0)
|
||||
- GCDWebServer
|
||||
- HLSCachingReverseProxyServer
|
||||
- PINCache
|
||||
- Cache (6.0.0)
|
||||
- Firebase/CoreOnly (8.14.0):
|
||||
- FirebaseCore (= 8.14.0)
|
||||
- Firebase/Messaging (8.14.0):
|
||||
- Firebase/CoreOnly
|
||||
- FirebaseMessaging (~> 8.14.0)
|
||||
- firebase_core (1.14.0):
|
||||
- Firebase/CoreOnly (= 8.14.0)
|
||||
- Flutter
|
||||
- firebase_messaging (11.2.12):
|
||||
- Firebase/Messaging (= 8.14.0)
|
||||
- firebase_core
|
||||
- Flutter
|
||||
- FirebaseCore (8.11.0):
|
||||
- FirebaseCore (8.14.0):
|
||||
- FirebaseCoreDiagnostics (~> 8.0)
|
||||
- GoogleUtilities/Environment (~> 7.7)
|
||||
- GoogleUtilities/Logger (~> 7.7)
|
||||
- FirebaseCoreDiagnostics (8.12.0):
|
||||
- FirebaseCoreDiagnostics (8.14.0):
|
||||
- GoogleDataTransport (~> 9.1)
|
||||
- GoogleUtilities/Environment (~> 7.7)
|
||||
- GoogleUtilities/Logger (~> 7.7)
|
||||
- nanopb (~> 2.30908.0)
|
||||
- FirebaseInstallations (8.12.0):
|
||||
- FirebaseInstallations (8.14.0):
|
||||
- FirebaseCore (~> 8.0)
|
||||
- GoogleUtilities/Environment (~> 7.7)
|
||||
- GoogleUtilities/UserDefaults (~> 7.7)
|
||||
- PromisesObjC (< 3.0, >= 1.2)
|
||||
- FirebaseMessaging (8.11.0):
|
||||
- FirebaseMessaging (8.14.0):
|
||||
- FirebaseCore (~> 8.0)
|
||||
- FirebaseInstallations (~> 8.0)
|
||||
- GoogleDataTransport (~> 9.1)
|
||||
|
|
@ -44,6 +51,9 @@ PODS:
|
|||
- FMDB (2.7.5):
|
||||
- FMDB/standard (= 2.7.5)
|
||||
- FMDB/standard (2.7.5)
|
||||
- GCDWebServer (3.5.4):
|
||||
- GCDWebServer/Core (= 3.5.4)
|
||||
- GCDWebServer/Core (3.5.4)
|
||||
- GoogleDataTransport (9.1.2):
|
||||
- GoogleUtilities/Environment (~> 7.2)
|
||||
- nanopb (~> 2.30908.0)
|
||||
|
|
@ -65,6 +75,9 @@ PODS:
|
|||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/UserDefaults (7.7.0):
|
||||
- GoogleUtilities/Logger
|
||||
- HLSCachingReverseProxyServer (0.1.0):
|
||||
- GCDWebServer (~> 3.5)
|
||||
- PINCache (>= 3.0.1-beta.3)
|
||||
- image_cropper (0.0.4):
|
||||
- Flutter
|
||||
- TOCropViewController (~> 2.6.1)
|
||||
|
|
@ -79,8 +92,16 @@ PODS:
|
|||
- nanopb/encode (2.30908.0)
|
||||
- path_provider_ios (0.0.1):
|
||||
- Flutter
|
||||
- permission_handler_apple (9.0.2):
|
||||
- permission_handler_apple (9.0.4):
|
||||
- Flutter
|
||||
- PINCache (3.0.3):
|
||||
- PINCache/Arc-exception-safe (= 3.0.3)
|
||||
- PINCache/Core (= 3.0.3)
|
||||
- PINCache/Arc-exception-safe (3.0.3):
|
||||
- PINCache/Core
|
||||
- PINCache/Core (3.0.3):
|
||||
- PINOperation (~> 1.2.1)
|
||||
- PINOperation (1.2.1)
|
||||
- PromisesObjC (2.0.0)
|
||||
- record (0.0.1):
|
||||
- Flutter
|
||||
|
|
@ -90,11 +111,16 @@ PODS:
|
|||
- TOCropViewController (2.6.1)
|
||||
- url_launcher_ios (0.0.1):
|
||||
- Flutter
|
||||
- video_player_avfoundation (0.0.1):
|
||||
- Flutter
|
||||
- wakelock (0.0.1):
|
||||
- Flutter
|
||||
- webview_flutter_wkwebview (0.0.1):
|
||||
- Flutter
|
||||
|
||||
DEPENDENCIES:
|
||||
- audio_session (from `.symlinks/plugins/audio_session/ios`)
|
||||
- better_player (from `.symlinks/plugins/better_player/ios`)
|
||||
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
||||
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
|
|
@ -108,25 +134,34 @@ DEPENDENCIES:
|
|||
- record (from `.symlinks/plugins/record/ios`)
|
||||
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`)
|
||||
- wakelock (from `.symlinks/plugins/wakelock/ios`)
|
||||
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- Cache
|
||||
- Firebase
|
||||
- FirebaseCore
|
||||
- FirebaseCoreDiagnostics
|
||||
- FirebaseInstallations
|
||||
- FirebaseMessaging
|
||||
- FMDB
|
||||
- GCDWebServer
|
||||
- GoogleDataTransport
|
||||
- GoogleUtilities
|
||||
- HLSCachingReverseProxyServer
|
||||
- nanopb
|
||||
- PINCache
|
||||
- PINOperation
|
||||
- PromisesObjC
|
||||
- TOCropViewController
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
audio_session:
|
||||
:path: ".symlinks/plugins/audio_session/ios"
|
||||
better_player:
|
||||
:path: ".symlinks/plugins/better_player/ios"
|
||||
firebase_core:
|
||||
:path: ".symlinks/plugins/firebase_core/ios"
|
||||
firebase_messaging:
|
||||
|
|
@ -153,37 +188,49 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/sqflite/ios"
|
||||
url_launcher_ios:
|
||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||
video_player_avfoundation:
|
||||
:path: ".symlinks/plugins/video_player_avfoundation/ios"
|
||||
wakelock:
|
||||
:path: ".symlinks/plugins/wakelock/ios"
|
||||
webview_flutter_wkwebview:
|
||||
:path: ".symlinks/plugins/webview_flutter_wkwebview/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
audio_session: 4f3e461722055d21515cf3261b64c973c062f345
|
||||
Firebase: 44dd9724c84df18b486639e874f31436eaa9a20c
|
||||
firebase_core: 08f6a85f62060111de5e98d6a214810d11365de9
|
||||
firebase_messaging: 36238f3d0b933af8c919aef608408aae06ba22e8
|
||||
FirebaseCore: 2f4f85b453cc8fea4bb2b37e370007d2bcafe3f0
|
||||
FirebaseCoreDiagnostics: 3b40dfadef5b90433a60ae01f01e90fe87aa76aa
|
||||
FirebaseInstallations: 25764cf322e77f99449395870a65b2bef88e1545
|
||||
FirebaseMessaging: 02e248e8997f71fa8cc9d78e9d49ec1a701ba14a
|
||||
better_player: 2406bfe8175203c7a46fa15f9d778d73b12e1646
|
||||
Cache: 4ca7e00363fca5455f26534e5607634c820ffc2d
|
||||
Firebase: 7e8fe528c161b9271d365217a74c16aaf834578e
|
||||
firebase_core: b0b382f1497ab407aceb25e41e3036c8798c1609
|
||||
firebase_messaging: 34dd10d1aa6d8f40d03660eeacd0452d62eec7aa
|
||||
FirebaseCore: b84a44ee7ba999e0f9f76d198a9c7f60a797b848
|
||||
FirebaseCoreDiagnostics: fd0c8490f34287229c1d6c103d3a55f81ec85712
|
||||
FirebaseInstallations: 7d1d967a307c12f1aadd76844fc321cef699b1ce
|
||||
FirebaseMessaging: 5ebc42d281567658a2cb72b9ef3506e4a1a1a6e4
|
||||
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
|
||||
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
|
||||
flutter_vibrate: 9f4c2ab57008965f78969472367c329dd77eb801
|
||||
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||
GCDWebServer: 2c156a56c8226e2d5c0c3f208a3621ccffbe3ce4
|
||||
GoogleDataTransport: 629c20a4d363167143f30ea78320d5a7eb8bd940
|
||||
GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1
|
||||
HLSCachingReverseProxyServer: 59935e1e0244ad7f3375d75b5ef46e8eb26ab181
|
||||
image_cropper: 60c2789d1f1a78c873235d4319ca0c34a69f2d98
|
||||
image_picker: 9aa50e1d8cdacdbed739e925b7eea16d014367e6
|
||||
image_picker: 541dcbb3b9cf32d87eacbd957845d8651d6c62c3
|
||||
just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa
|
||||
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
|
||||
path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5
|
||||
permission_handler_apple: d21b38e1a4b2e041c63af9568f9165e114e507a6
|
||||
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
|
||||
permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce
|
||||
PINCache: 7a8fc1a691173d21dbddbf86cd515de6efa55086
|
||||
PINOperation: 00c935935f1e8cf0d1e2d6b542e75b88fc3e5e20
|
||||
PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58
|
||||
record: 7ee2393532f8553bbb09fa19e95478323b7c0a99
|
||||
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
|
||||
TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863
|
||||
url_launcher_ios: 02f1989d4e14e998335b02b67a7590fa34f971af
|
||||
url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de
|
||||
video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff
|
||||
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
|
||||
webview_flutter_wkwebview: 005fbd90c888a42c5690919a1527ecc6649e1162
|
||||
|
||||
PODFILE CHECKSUM: fe0e1ee7f3d1f7d00b11b474b62dd62134535aea
|
||||
PODFILE CHECKSUM: 7368163408c647b7eb699d0d788ba6718e18fb8d
|
||||
|
||||
COCOAPODS: 1.11.2
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:didvan/config/design_config.dart';
|
||||
import 'package:better_player/better_player.dart';
|
||||
import 'package:didvan/models/view/app_bar_data.dart';
|
||||
import 'package:didvan/services/media/media.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/didvan/app_bar.dart';
|
||||
import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
|
@ -26,61 +25,23 @@ class StudioDetails extends StatefulWidget {
|
|||
|
||||
class _StudioDetailsState extends State<StudioDetails> {
|
||||
final _scrollController = ScrollController();
|
||||
bool _isFullScreen = false;
|
||||
bool _isInit = true;
|
||||
|
||||
double _dwInPortrait = 0;
|
||||
double _scaleInPortrait = 1;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final state = context.read<StudioDetailsState>();
|
||||
state.args = widget.pageData['args'];
|
||||
Future.delayed(
|
||||
Duration.zero,
|
||||
() => state.getStudioDetails(widget.pageData['id']),
|
||||
);
|
||||
state.args = widget.pageData['args'];
|
||||
|
||||
if (Platform.isAndroid) WebView.platform = AndroidWebView();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
Future<void> _changeFullSceen(bool value) async {
|
||||
if (value) {
|
||||
await SystemChrome.setEnabledSystemUIMode(
|
||||
SystemUiMode.manual,
|
||||
overlays: [],
|
||||
);
|
||||
SystemChrome.setSystemUIOverlayStyle(
|
||||
const SystemUiOverlayStyle(
|
||||
systemNavigationBarColor: Colors.black,
|
||||
),
|
||||
);
|
||||
await SystemChrome.setPreferredOrientations(
|
||||
[DeviceOrientation.landscapeLeft],
|
||||
);
|
||||
} else {
|
||||
await SystemChrome.setEnabledSystemUIMode(
|
||||
SystemUiMode.manual,
|
||||
overlays: [SystemUiOverlay.bottom, SystemUiOverlay.top],
|
||||
);
|
||||
await SystemChrome.setPreferredOrientations(
|
||||
[DeviceOrientation.portraitUp],
|
||||
);
|
||||
DesignConfig.updateSystemUiOverlayStyle();
|
||||
}
|
||||
setState(() {
|
||||
_isFullScreen = value;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ds = MediaQuery.of(context).size;
|
||||
if (_isInit) {
|
||||
_dwInPortrait = MediaQuery.of(context).size.width;
|
||||
_scaleInPortrait = _dwInPortrait / 576;
|
||||
_isInit = false;
|
||||
}
|
||||
final d = MediaQuery.of(context);
|
||||
|
||||
return Consumer<StudioDetailsState>(
|
||||
builder: (context, state, child) => StateHandler<StudioDetailsState>(
|
||||
|
|
@ -95,23 +56,19 @@ class _StudioDetailsState extends State<StudioDetails> {
|
|||
builder: (context, state) {
|
||||
return WillPopScope(
|
||||
onWillPop: () async {
|
||||
if (_isFullScreen) {
|
||||
await _changeFullSceen(false);
|
||||
return false;
|
||||
}
|
||||
if (MediaService.currentPodcast != null) {
|
||||
state.studio = MediaService.currentPodcast!;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
child: DidvanScaffold(
|
||||
key: ValueKey(state.studio.id),
|
||||
scrollController: _scrollController,
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
padding: EdgeInsets.zero,
|
||||
appBarData: _isFullScreen
|
||||
? null
|
||||
: AppBarData(
|
||||
child: SafeArea(
|
||||
child: Scaffold(
|
||||
key: ValueKey(state.studio.id),
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
appBar: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(56),
|
||||
child: DidvanAppBar(
|
||||
appBarData: AppBarData(
|
||||
trailing: BookmarkButton(
|
||||
itemId: state.studio.id,
|
||||
type: 'video',
|
||||
|
|
@ -125,103 +82,41 @@ class _StudioDetailsState extends State<StudioDetails> {
|
|||
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,
|
||||
height: _isFullScreen ? ds.height : ds.width * 9 / 16,
|
||||
child: Stack(
|
||||
children: [
|
||||
WebView(
|
||||
zoomEnabled: false,
|
||||
backgroundColor: Colors.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,
|
||||
),
|
||||
Positioned(
|
||||
right: 42,
|
||||
bottom: 8,
|
||||
child: GestureDetector(
|
||||
onTap: () => _changeFullSceen(!_isFullScreen),
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
width: 24,
|
||||
height: 30,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
DetailsTabBar(
|
||||
isVideo: true,
|
||||
onCommentsTabSelected: () => Future.delayed(
|
||||
const Duration(milliseconds: 100),
|
||||
() => _scrollController.animateTo(
|
||||
_scrollController.position.maxScrollExtent,
|
||||
duration: DesignConfig.lowAnimationDuration,
|
||||
curve: Curves.easeIn,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
children: [
|
||||
StudioDetailsWidget(
|
||||
scrollController: _scrollController,
|
||||
onMarkChanged: (id, value) =>
|
||||
widget.pageData['onMarkChanged'](id, value, true),
|
||||
body: Column(
|
||||
children: [
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: BetterPlayer.network(
|
||||
'https://studio-didvan.arvanvod.com/Vz01Bxq2bQ/nylPWJ4B63/h_,144_200,240_400,360_800,480_1215,720_1215,k.mp4.list/master.m3u8',
|
||||
betterPlayerConfiguration:
|
||||
const BetterPlayerConfiguration(
|
||||
aspectRatio: 16 / 9,
|
||||
controlsConfiguration:
|
||||
BetterPlayerControlsConfiguration(
|
||||
enablePlaybackSpeed: false,
|
||||
enableSubtitles: false,
|
||||
enableAudioTracks: false,
|
||||
),
|
||||
autoDetectFullscreenAspectRatio: true,
|
||||
autoDetectFullscreenDeviceOrientation: true,
|
||||
fullScreenAspectRatio: 16 / 9,
|
||||
),
|
||||
),
|
||||
),
|
||||
const DetailsTabBar(
|
||||
isVideo: true,
|
||||
),
|
||||
Expanded(
|
||||
child: StudioDetailsWidget(
|
||||
onMarkChanged: (id, value) =>
|
||||
widget.pageData['onMarkChanged'](id, value, true),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -24,18 +24,17 @@ class StudioDetails extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _StudioDetailsState extends State<StudioDetails> {
|
||||
final _scrollController = ScrollController();
|
||||
|
||||
bool _isFullScreen = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final state = context.read<StudioDetailsState>();
|
||||
state.args = widget.pageData['args'];
|
||||
Future.delayed(
|
||||
Duration.zero,
|
||||
() => state.getStudioDetails(widget.pageData['id']),
|
||||
);
|
||||
state.args = widget.pageData['args'];
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
|
@ -100,7 +99,6 @@ class _StudioDetailsState extends State<StudioDetails> {
|
|||
return true;
|
||||
},
|
||||
child: DidvanScaffold(
|
||||
scrollController: _scrollController,
|
||||
padding: EdgeInsets.zero,
|
||||
appBarData: _isFullScreen
|
||||
? null
|
||||
|
|
@ -127,21 +125,13 @@ class _StudioDetailsState extends State<StudioDetails> {
|
|||
72 -
|
||||
MediaQuery.of(context).padding.top,
|
||||
flexibleSpace: Column(
|
||||
children: [
|
||||
const AspectRatio(
|
||||
children: const [
|
||||
AspectRatio(
|
||||
aspectRatio: 16 / 9,
|
||||
child: HtmlElementView(viewType: 'video'),
|
||||
),
|
||||
DetailsTabBar(
|
||||
isVideo: true,
|
||||
onCommentsTabSelected: () => Future.delayed(
|
||||
const Duration(milliseconds: 100),
|
||||
() => _scrollController.animateTo(
|
||||
_scrollController.position.maxScrollExtent,
|
||||
duration: DesignConfig.lowAnimationDuration,
|
||||
curve: Curves.easeIn,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -149,7 +139,6 @@ class _StudioDetailsState extends State<StudioDetails> {
|
|||
],
|
||||
children: [
|
||||
StudioDetailsWidget(
|
||||
scrollController: _scrollController,
|
||||
onMarkChanged: widget.pageData['onMarkChanged'],
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -8,12 +8,10 @@ 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
|
||||
|
|
@ -55,7 +53,6 @@ class DetailsTabBar extends StatelessWidget {
|
|||
title: 'نظرات',
|
||||
onTap: () {
|
||||
state.selectedDetailsIndex = 1;
|
||||
onCommentsTabSelected();
|
||||
},
|
||||
isSelected: state.selectedDetailsIndex == 1,
|
||||
isVideo: isVideo,
|
||||
|
|
|
|||
|
|
@ -19,165 +19,164 @@ import 'package:provider/provider.dart';
|
|||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class StudioDetailsWidget extends StatelessWidget {
|
||||
final ScrollController? scrollController;
|
||||
final VoidCallback? onCommentsTabSelected;
|
||||
final void Function(int id, bool value) onMarkChanged;
|
||||
const StudioDetailsWidget({
|
||||
Key? key,
|
||||
required this.onMarkChanged,
|
||||
this.onCommentsTabSelected,
|
||||
this.scrollController,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ds = MediaQuery.of(context).size;
|
||||
return Consumer<StudioDetailsState>(
|
||||
builder: (context, state, child) {
|
||||
bool isVideo = state.studio.media.contains('iframe');
|
||||
return Container(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (!isVideo)
|
||||
DetailsTabBar(
|
||||
isVideo: isVideo,
|
||||
onCommentsTabSelected: onCommentsTabSelected ?? () {},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: ds.height -
|
||||
ds.width * 9 / 16 -
|
||||
144 -
|
||||
MediaQuery.of(context).padding.top,
|
||||
),
|
||||
child: StateHandler<StudioDetailsState>(
|
||||
onRetry: () {},
|
||||
state: state,
|
||||
builder: (context, state) {
|
||||
if (state.selectedDetailsIndex == 0) {
|
||||
return SingleChildScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Html(
|
||||
key: ValueKey(state.studio.id),
|
||||
data: state.studio.description,
|
||||
onAnchorTap: (href, context, map, element) =>
|
||||
launch(href!),
|
||||
style: {
|
||||
'*': Style(
|
||||
direction: TextDirection.rtl,
|
||||
textAlign: TextAlign.right,
|
||||
lineHeight: LineHeight.percent(135),
|
||||
margin: EdgeInsets.zero,
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
},
|
||||
),
|
||||
if (state.studio.tags.isNotEmpty)
|
||||
return SafeArea(
|
||||
bottom: true,
|
||||
child: Consumer<StudioDetailsState>(
|
||||
builder: (context, state, child) {
|
||||
bool isVideo = state.studio.media.contains('iframe');
|
||||
return Container(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (!isVideo)
|
||||
DetailsTabBar(
|
||||
isVideo: isVideo,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: isVideo
|
||||
? double.infinity
|
||||
: ds.height -
|
||||
ds.width * 9 / 16 -
|
||||
144 -
|
||||
MediaQuery.of(context).padding.top,
|
||||
),
|
||||
child: StateHandler<StudioDetailsState>(
|
||||
onRetry: () {},
|
||||
state: state,
|
||||
builder: (context, state) {
|
||||
if (state.selectedDetailsIndex == 0) {
|
||||
return SingleChildScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Html(
|
||||
key: ValueKey(state.studio.id),
|
||||
data: state.studio.description,
|
||||
onAnchorTap: (href, context, map, element) =>
|
||||
launch(href!),
|
||||
style: {
|
||||
'*': Style(
|
||||
direction: TextDirection.rtl,
|
||||
textAlign: TextAlign.right,
|
||||
lineHeight: LineHeight.percent(135),
|
||||
margin: EdgeInsets.zero,
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
},
|
||||
),
|
||||
if (state.studio.tags.isNotEmpty)
|
||||
const SizedBox(height: 20),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
for (var i = 0;
|
||||
i < state.studio.tags.length;
|
||||
i++)
|
||||
TagItem(
|
||||
tag: state.studio.tags[i],
|
||||
onMarkChanged: (id, value) =>
|
||||
_onMarkChanged(id, value, state),
|
||||
type: isVideo ? 'video' : 'podcast',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
for (var i = 0;
|
||||
i < state.studio.tags.length;
|
||||
i++)
|
||||
TagItem(
|
||||
tag: state.studio.tags[i],
|
||||
onMarkChanged: (id, value) =>
|
||||
_onMarkChanged(id, value, state),
|
||||
type: isVideo ? 'video' : 'podcast',
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const SizedBox(),
|
||||
if (state.nextStudio != null &&
|
||||
state.alongSideState == AppState.idle)
|
||||
_StudioPreview(
|
||||
isNext: true,
|
||||
studio: state.nextStudio!,
|
||||
),
|
||||
if (state.alongSideState == AppState.busy)
|
||||
_StudioPreview.placeHolder,
|
||||
if (state.prevStudio != null &&
|
||||
state.alongSideState == AppState.idle)
|
||||
_StudioPreview(
|
||||
isNext: false,
|
||||
studio: state.prevStudio!,
|
||||
),
|
||||
if (state.alongSideState == AppState.busy)
|
||||
_StudioPreview.placeHolder,
|
||||
const SizedBox(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
if (state.selectedDetailsIndex == 1) {
|
||||
return ChangeNotifierProvider<CommentsState>(
|
||||
create: (context) => CommentsState(),
|
||||
child: Comments(
|
||||
pageData: {
|
||||
'id': state.studio.id,
|
||||
'type': 'studio',
|
||||
'title': state.studio.title,
|
||||
'onCommentsChanged': state.onCommentsChanged,
|
||||
'isPage': false,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
if (state.studio.relatedContents.isEmpty)
|
||||
for (var i = 0; i < 3; i++)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 8,
|
||||
left: 16,
|
||||
right: 16,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const SizedBox(),
|
||||
if (state.nextStudio != null &&
|
||||
state.alongSideState == AppState.idle)
|
||||
_StudioPreview(
|
||||
isNext: true,
|
||||
studio: state.nextStudio!,
|
||||
scrollController: scrollController,
|
||||
),
|
||||
if (state.alongSideState == AppState.busy)
|
||||
_StudioPreview.placeHolder,
|
||||
if (state.prevStudio != null &&
|
||||
state.alongSideState == AppState.idle)
|
||||
_StudioPreview(
|
||||
isNext: false,
|
||||
studio: state.prevStudio!,
|
||||
scrollController: scrollController,
|
||||
),
|
||||
if (state.alongSideState == AppState.busy)
|
||||
_StudioPreview.placeHolder,
|
||||
const SizedBox(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
if (state.selectedDetailsIndex == 1) {
|
||||
return ChangeNotifierProvider<CommentsState>(
|
||||
create: (context) => CommentsState(),
|
||||
child: Comments(
|
||||
pageData: {
|
||||
'id': state.studio.id,
|
||||
'type': 'studio',
|
||||
'title': state.studio.title,
|
||||
'onCommentsChanged': state.onCommentsChanged,
|
||||
'isPage': false,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
if (state.studio.relatedContents.isEmpty)
|
||||
for (var i = 0; i < 3; i++)
|
||||
child: MultitypeOverview.placeholder,
|
||||
),
|
||||
for (var i = 0;
|
||||
i < state.studio.relatedContents.length;
|
||||
i++)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 8,
|
||||
left: 16,
|
||||
right: 16,
|
||||
),
|
||||
child: MultitypeOverview.placeholder,
|
||||
child: MultitypeOverview(
|
||||
item: state.studio.relatedContents[i],
|
||||
onMarkChanged: (id, value) {},
|
||||
),
|
||||
),
|
||||
for (var i = 0;
|
||||
i < state.studio.relatedContents.length;
|
||||
i++)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 8,
|
||||
left: 16,
|
||||
right: 16,
|
||||
),
|
||||
child: MultitypeOverview(
|
||||
item: state.studio.relatedContents[i],
|
||||
onMarkChanged: (id, value) {},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -196,13 +195,11 @@ class StudioDetailsWidget extends StatelessWidget {
|
|||
class _StudioPreview extends StatelessWidget {
|
||||
final bool isNext;
|
||||
final StudioDetailsData studio;
|
||||
final ScrollController? scrollController;
|
||||
const _StudioPreview(
|
||||
{Key? key,
|
||||
required this.isNext,
|
||||
required this.studio,
|
||||
this.scrollController})
|
||||
: super(key: key);
|
||||
const _StudioPreview({
|
||||
Key? key,
|
||||
required this.isNext,
|
||||
required this.studio,
|
||||
}) : super(key: key);
|
||||
|
||||
String get _previewTitle {
|
||||
if (studio.media.contains('iframe')) {
|
||||
|
|
@ -221,11 +218,6 @@ class _StudioPreview extends StatelessWidget {
|
|||
args: state.args,
|
||||
isForward: isNext,
|
||||
);
|
||||
scrollController?.animateTo(
|
||||
0,
|
||||
duration: DesignConfig.lowAnimationDuration,
|
||||
curve: Curves.easeIn,
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
width: 88,
|
||||
|
|
|
|||
|
|
@ -339,12 +339,6 @@ class _PlayerNavBar extends StatelessWidget {
|
|||
),
|
||||
)
|
||||
: StudioDetailsWidget(
|
||||
onCommentsTabSelected: () {
|
||||
Future.delayed(
|
||||
const Duration(milliseconds: 100),
|
||||
sheetKey.currentState?.expand,
|
||||
);
|
||||
},
|
||||
onMarkChanged: (id, value) =>
|
||||
context.read<StudioState>().changeMark(id, value, true),
|
||||
),
|
||||
|
|
|
|||
63
pubspec.lock
63
pubspec.lock
|
|
@ -22,6 +22,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.10.0"
|
||||
better_player:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: better_player
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.0.81"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -315,6 +322,20 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_widget_from_html_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_widget_from_html_core
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.8.5+1"
|
||||
fwfh_text_style:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fwfh_text_style
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.7.3+1"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -824,6 +845,48 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
visibility_detector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: visibility_detector
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.2"
|
||||
wakelock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: wakelock
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.5.6"
|
||||
wakelock_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: wakelock_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.0"
|
||||
wakelock_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: wakelock_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
wakelock_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: wakelock_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.0"
|
||||
wakelock_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: wakelock_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
webview_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ dependencies:
|
|||
webview_flutter: ^3.0.1
|
||||
expandable_bottom_sheet: ^1.1.1+1
|
||||
permission_handler: ^9.2.0
|
||||
better_player: ^0.0.81
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
|
|
|
|||
Loading…
Reference in New Issue