diff --git a/lib/widgets/didvan/page_view.dart b/lib/widgets/didvan/page_view.dart index 77f91ad..0f54599 100644 --- a/lib/widgets/didvan/page_view.dart +++ b/lib/widgets/didvan/page_view.dart @@ -1,9 +1,14 @@ import 'package:carousel_slider/carousel_slider.dart'; +import 'package:didvan/config/design_config.dart'; +import 'package:didvan/config/theme_data.dart'; +import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/pages/home/widgets/multitype_overview.dart'; import 'package:didvan/utils/date_time.dart'; +import 'package:didvan/widgets/animated_visibility.dart'; import 'package:didvan/widgets/didvan/card.dart'; import 'package:didvan/widgets/didvan/divider.dart'; import 'package:didvan/widgets/didvan/text.dart'; +import 'package:didvan/widgets/ink_wrapper.dart'; import 'package:didvan/widgets/item_title.dart'; import 'package:didvan/widgets/skeleton_image.dart'; import 'package:didvan/pages/home/widgets/tag_item.dart'; @@ -36,124 +41,140 @@ class _DidvanPageViewState extends State { @override Widget build(BuildContext context) { final double deviceTopPadding = MediaQuery.of(context).padding.top; - return Directionality( - textDirection: TextDirection.ltr, - child: CarouselSlider.builder( - itemCount: widget.items.length, - options: CarouselOptions( - onPageChanged: (index, reason) => widget.onPageChanged(index), - height: double.infinity, - initialPage: widget.initialIndex, - viewportFraction: 0.94, - enableInfiniteScroll: false, - ), - itemBuilder: (context, index, realIndex) => Directionality( - textDirection: TextDirection.rtl, - child: SizedBox( - height: MediaQuery.of(context).size.height, - child: SingleChildScrollView( - controller: - index == widget.currentIndex ? widget.scrollController : null, - physics: const BouncingScrollPhysics(), - padding: EdgeInsets.only( - left: 4, - right: 4, - top: 16 + deviceTopPadding, - bottom: 92, - ), - child: DidvanCard( - padding: EdgeInsets.zero, - enableBorder: false, - child: Builder( - builder: (context) { - final item = widget.items[index]; - if (item == null) { - return const SizedBox(); - } - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SkeletonImage( - imageUrl: item.image, - aspectRatio: 16 / 9, - ), - const SizedBox(height: 20), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: DidvanText( - item.title, - style: Theme.of(context).textTheme.subtitle1, - ), - ), - const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: _subtitle(item), - ), - for (var i = 0; i < item.contents.length; i++) - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 4, + return Stack( + children: [ + Directionality( + textDirection: TextDirection.ltr, + child: CarouselSlider.builder( + itemCount: widget.items.length, + options: CarouselOptions( + onPageChanged: (index, reason) => widget.onPageChanged(index), + height: double.infinity, + initialPage: widget.initialIndex, + viewportFraction: 0.94, + enableInfiniteScroll: false, + ), + itemBuilder: (context, index, realIndex) => Directionality( + textDirection: TextDirection.rtl, + child: SizedBox( + height: MediaQuery.of(context).size.height, + child: SingleChildScrollView( + controller: index == widget.currentIndex + ? widget.scrollController + : null, + physics: const BouncingScrollPhysics(), + padding: EdgeInsets.only( + left: 4, + right: 4, + top: 16 + deviceTopPadding, + bottom: 92, + ), + child: DidvanCard( + padding: EdgeInsets.zero, + enableBorder: false, + child: Builder( + builder: (context) { + final item = widget.items[index]; + if (item == null) { + return const SizedBox(); + } + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SkeletonImage( + imageUrl: item.image, + aspectRatio: 16 / 9, ), - child: _contentBuilder(item, i), - ), - if (item.tags.isNotEmpty) const SizedBox(height: 20), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Wrap( - spacing: 8, - runSpacing: 8, - children: [ - for (var i = 0; i < item.tags.length; i++) - TagItem(label: item.tags[i].label), - ], - ), - ), - const Padding( - padding: EdgeInsets.only( - left: 16, - right: 16, - top: 16, - ), - child: DidvanDivider(verticalPadding: 0), - ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 16.0), - child: ItemTitle(title: 'مطالب مشابه'), - ), - if (item.relatedContents.isEmpty) - for (var i = 0; i < 3; i++) + const SizedBox(height: 20), Padding( - padding: const EdgeInsets.only( - bottom: 8, + padding: + const EdgeInsets.symmetric(horizontal: 16), + child: DidvanText( + item.title, + style: Theme.of(context).textTheme.subtitle1, + ), + ), + const SizedBox(height: 8), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 16), + child: _subtitle(item), + ), + for (var i = 0; i < item.contents.length; i++) + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 4, + ), + child: _contentBuilder(item, i), + ), + if (item.tags.isNotEmpty) + const SizedBox(height: 20), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 16), + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + for (var i = 0; i < item.tags.length; i++) + TagItem(label: item.tags[i].label), + ], + ), + ), + const Padding( + padding: EdgeInsets.only( left: 16, right: 16, + top: 16, ), - child: MultitypeOverview.placeholder, + child: DidvanDivider(verticalPadding: 0), ), - for (var i = 0; i < item.relatedContents.length; i++) - Padding( - padding: const EdgeInsets.only( - bottom: 8, - left: 16, - right: 16, + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16.0), + child: ItemTitle(title: 'مطالب مشابه'), ), - child: MultitypeOverview( - item: item.relatedContents[i], - onMarkChanged: (id, value) {}, - ), - ), - const SizedBox(height: 8), - ], - ); - }, + if (item.relatedContents.isEmpty) + for (var i = 0; i < 3; i++) + Padding( + padding: const EdgeInsets.only( + bottom: 8, + left: 16, + right: 16, + ), + child: MultitypeOverview.placeholder, + ), + for (var i = 0; + i < item.relatedContents.length; + i++) + Padding( + padding: const EdgeInsets.only( + bottom: 8, + left: 16, + right: 16, + ), + child: MultitypeOverview( + item: item.relatedContents[i], + onMarkChanged: (id, value) {}, + ), + ), + const SizedBox(height: 8), + ], + ); + }, + ), + ), ), ), ), ), ), - ), + Positioned( + child: _BackButton(scrollController: widget.scrollController), + right: 24, + top: 24 + deviceTopPadding, + ), + ], ); } @@ -219,3 +240,66 @@ class _DidvanPageViewState extends State { } } } + +class _BackButton extends StatefulWidget { + final ScrollController scrollController; + const _BackButton({Key? key, required this.scrollController}) + : super(key: key); + + @override + __BackButtonState createState() => __BackButtonState(); +} + +class __BackButtonState extends State<_BackButton> { + bool _isVisible = false; + + @override + void didUpdateWidget(covariant _BackButton oldWidget) { + _isVisible = false; + _handleScroll(); + super.didUpdateWidget(oldWidget); + } + + @override + void initState() { + _handleScroll(); + super.initState(); + } + + void _handleScroll() { + widget.scrollController.addListener(() { + final position = widget.scrollController.position.pixels; + final size = MediaQuery.of(context).size.height * 0.3; + if (position > size && _isVisible == false) { + setState(() { + _isVisible = true; + }); + } + if (position < size && _isVisible == true) { + setState(() { + _isVisible = false; + }); + } + }); + } + + @override + Widget build(BuildContext context) { + return AnimatedVisibility( + duration: DesignConfig.lowAnimationDuration, + isVisible: _isVisible, + child: InkWrapper( + borderRadius: DesignConfig.lowBorderRadius, + onPressed: Navigator.of(context).pop, + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.splash, + border: Border.all(color: Theme.of(context).colorScheme.border), + borderRadius: DesignConfig.lowBorderRadius, + ), + child: const Icon(DidvanIcons.back_regular, size: 32), + ), + ), + ); + } +}