diff --git a/lib/widgets/bookmark_button.dart b/lib/widgets/bookmark_button.dart index 6ef9ca0..c7a72cd 100644 --- a/lib/widgets/bookmark_button.dart +++ b/lib/widgets/bookmark_button.dart @@ -6,12 +6,14 @@ class BookmarkButton extends StatefulWidget { final bool value; final VoidCallback onMark; final VoidCallback onUnmark; - const BookmarkButton( - {Key? key, - required this.value, - required this.onMark, - required this.onUnmark}) - : super(key: key); + final bool bigGestureSize; + const BookmarkButton({ + Key? key, + required this.value, + required this.onMark, + required this.onUnmark, + this.bigGestureSize = false, + }) : super(key: key); @override State createState() => _BookmarkButtonState(); @@ -35,7 +37,7 @@ class _BookmarkButtonState extends State { @override Widget build(BuildContext context) { return DidvanIconButton( - gestureSize: 24, + gestureSize: widget.bigGestureSize ? 32 : 24, icon: _value ? DidvanIcons.bookmark_solid : DidvanIcons.bookmark_regular, onPressed: () { setState(() { diff --git a/lib/widgets/didvan/button.dart b/lib/widgets/didvan/button.dart index 7b3122b..9160709 100644 --- a/lib/widgets/didvan/button.dart +++ b/lib/widgets/didvan/button.dart @@ -7,14 +7,18 @@ import 'package:flutter/material.dart'; class DidvanButton extends StatelessWidget { final VoidCallback? onPressed; final String? title; + final double? width; final ButtonStyleMode style; final bool enabled; + final double? height; const DidvanButton({ Key? key, this.onPressed, this.title, this.style = ButtonStyleMode.primary, this.enabled = true, + this.width, + this.height = 48, }) : super(key: key); @override @@ -42,8 +46,8 @@ class DidvanButton extends StatelessWidget { default: } return SizedBox( - height: 48, - width: double.infinity, + height: height, + width: width ?? double.infinity, child: MaterialButton( shape: RoundedRectangleBorder( borderRadius: DesignConfig.lowBorderRadius, diff --git a/lib/widgets/didvan/chip.dart b/lib/widgets/didvan/chip.dart new file mode 100644 index 0000000..358bcef --- /dev/null +++ b/lib/widgets/didvan/chip.dart @@ -0,0 +1,35 @@ +import 'package:didvan/config/design_config.dart'; +import 'package:didvan/config/theme_data.dart'; +import 'package:didvan/widgets/didvan/text.dart'; +import 'package:didvan/widgets/ink_wrapper.dart'; +import 'package:flutter/material.dart'; + +class DidvanChip extends StatelessWidget { + final String label; + final VoidCallback? onTap; + const DidvanChip({ + Key? key, + required this.label, + this.onTap, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return InkWrapper( + borderRadius: DesignConfig.highBorderRadius, + onPressed: onTap, + child: Container( + padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 8), + decoration: BoxDecoration( + borderRadius: DesignConfig.highBorderRadius, + color: Theme.of(context).colorScheme.primary, + ), + child: DidvanText( + label, + style: Theme.of(context).textTheme.caption, + color: Theme.of(context).colorScheme.white, + ), + ), + ); + } +} diff --git a/lib/widgets/didvan/page_view.dart b/lib/widgets/didvan/page_view.dart index f16d70b..f4cbfa5 100644 --- a/lib/widgets/didvan/page_view.dart +++ b/lib/widgets/didvan/page_view.dart @@ -1,13 +1,19 @@ import 'package:carousel_slider/carousel_slider.dart'; +import 'package:didvan/utils/date_time.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/skeleton_image.dart'; +import 'package:didvan/widgets/tag_item.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_html/flutter_html.dart'; class DidvanPageView extends StatefulWidget { final List items; final int initialIndex; + final bool isRadar; final ScrollController? scrollController; + final void Function(int index) onPageChanged; const DidvanPageView({ Key? key, @@ -15,6 +21,7 @@ class DidvanPageView extends StatefulWidget { required this.items, this.scrollController, required this.onPageChanged, + required this.isRadar, }) : super(key: key); @override @@ -36,62 +43,76 @@ class _DidvanPageViewState extends State { viewportFraction: 0.94, enableInfiniteScroll: false, ), - itemBuilder: (context, index, realIndex) => SizedBox( - height: MediaQuery.of(context).size.height, - child: SingleChildScrollView( - controller: index == 1 ? 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), - for (var i = 0; i < item.contents.length; i++) + itemBuilder: (context, index, realIndex) => Directionality( + textDirection: TextDirection.rtl, + child: SizedBox( + height: MediaQuery.of(context).size.height, + child: SingleChildScrollView( + controller: index == 1 ? 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, - vertical: 4, - ), - child: Builder( - builder: (context) { - final content = item.contents[i]; - if (content.text != null) { - return DidvanText( - item.contents[i].text!, - ); - } - if (content.image != null) { - return SkeletonImage( - imageUrl: content.image!, - aspectRatio: 16 / 9, - ); - } - return const SizedBox(); - }, + padding: const EdgeInsets.symmetric(horizontal: 16), + child: DidvanText( + item.title, + style: Theme.of(context).textTheme.subtitle1, ), ), - const SizedBox(height: 20), - ], - ); - }, + 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.symmetric(horizontal: 16), + child: DidvanDivider(), + ), + ], + ); + }, + ), ), ), ), @@ -99,4 +120,53 @@ class _DidvanPageViewState extends State { ), ); } + + Widget _contentBuilder(dynamic item, int index) { + final content = item.contents[index]; + if (content.text != null) { + return Html(data: item.contents[index].text!); + } + if (content.image != null) { + return SkeletonImage( + imageUrl: content.image!, + aspectRatio: 16 / 9, + ); + } + return const SizedBox(); + } + + Widget _subtitle(dynamic item) { + if (widget.isRadar) { + return Wrap( + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + DidvanText( + 'رادار ', + style: Theme.of(context).textTheme.caption, + ), + for (var i = 0; i < item.categories.length; i++) + DidvanText( + item.categories[i].label + + ' - ' + + DateTimeUtils.momentGenerator(item.createdAt) + + '${i != item.categories.length - 1 ? '،' : ''} ', + style: Theme.of(context).textTheme.caption, + ), + ], + ); + } else { + return Row( + children: [ + DidvanText( + item.reference, + style: Theme.of(context).textTheme.caption, + ), + DidvanText( + ' - ' + DateTimeUtils.momentGenerator(item.createdAt), + style: Theme.of(context).textTheme.caption, + ), + ], + ); + } + } } diff --git a/lib/widgets/didvan/scaffold.dart b/lib/widgets/didvan/scaffold.dart index 55f7d27..6e00a7a 100644 --- a/lib/widgets/didvan/scaffold.dart +++ b/lib/widgets/didvan/scaffold.dart @@ -7,25 +7,29 @@ class DidvanScaffold extends StatelessWidget { final List? children; final AppBarData appBarData; final EdgeInsets padding; + final Color? backgroundColor; const DidvanScaffold({ Key? key, this.slivers, required this.appBarData, this.children, this.padding = const EdgeInsets.symmetric(horizontal: 16), + this.backgroundColor, }) : super(key: key); @override Widget build(BuildContext context) { final double statusBarHeight = MediaQuery.of(context).padding.top; return Scaffold( + backgroundColor: backgroundColor, body: Padding( padding: EdgeInsets.only(top: statusBarHeight), child: CustomScrollView( slivers: [ SliverAppBar( toolbarHeight: kToolbarHeight, - backgroundColor: Theme.of(context).backgroundColor, + backgroundColor: + backgroundColor ?? Theme.of(context).colorScheme.background, automaticallyImplyLeading: false, pinned: true, flexibleSpace: DidvanAppBar(appBarData: appBarData), @@ -43,7 +47,12 @@ class DidvanScaffold extends StatelessWidget { ), ), ), - if (slivers != null) ...slivers!, + if (slivers != null) + for (var i = 0; i < slivers!.length; i++) + SliverPadding( + padding: padding, + sliver: slivers![i], + ), ], ), ), diff --git a/lib/widgets/didvan/text.dart b/lib/widgets/didvan/text.dart index 286b1b7..13dc23d 100644 --- a/lib/widgets/didvan/text.dart +++ b/lib/widgets/didvan/text.dart @@ -9,6 +9,7 @@ class DidvanText extends StatelessWidget { final TextAlign textAlign; final int? maxLines; final bool isEnglishFont; + final TextOverflow? overflow; const DidvanText( this.text, { @@ -20,6 +21,7 @@ class DidvanText extends StatelessWidget { this.textAlign = TextAlign.right, this.maxLines, this.isEnglishFont = false, + this.overflow, }) : super(key: key); @override @@ -31,6 +33,7 @@ class DidvanText extends StatelessWidget { fontWeight: fontWeight, fontSize: fontSize, )).copyWith(fontFamily: isEnglishFont ? 'Dana' : null), + overflow: overflow, textAlign: textAlign, maxLines: maxLines, ); diff --git a/lib/widgets/floating_navigation_bar.dart b/lib/widgets/floating_navigation_bar.dart index 9325abb..9a9bf97 100644 --- a/lib/widgets/floating_navigation_bar.dart +++ b/lib/widgets/floating_navigation_bar.dart @@ -8,9 +8,11 @@ import 'package:didvan/pages/home/settings/widgets/menu_item.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/widgets/bookmark_button.dart'; +import 'package:didvan/widgets/didvan/chip.dart'; import 'package:didvan/widgets/didvan/divider.dart'; import 'package:didvan/widgets/didvan/icon_button.dart'; import 'package:didvan/widgets/didvan/text.dart'; +import 'package:didvan/widgets/item_title.dart'; import 'package:flutter/material.dart'; class FloatingNavigationBar extends StatefulWidget { @@ -116,6 +118,7 @@ class _FloatingNavigationBarState extends State { value: _item.marked, onMark: widget.onMark, onUnmark: widget.onUnmark, + bigGestureSize: true, ), SizedBox( width: 48, @@ -127,7 +130,6 @@ class _FloatingNavigationBarState extends State { _item.comments.toString(), color: foregroundColor, ), - const SizedBox(width: 4), DidvanIconButton( gestureSize: 32, onPressed: () => Navigator.of(context).pushNamed( @@ -149,6 +151,7 @@ class _FloatingNavigationBarState extends State { value: _item.marked, onMark: widget.onMark, onUnmark: widget.onUnmark, + bigGestureSize: true, ), if (_isRadar) DidvanIconButton( @@ -166,16 +169,43 @@ class _FloatingNavigationBarState extends State { ActionSheetUtils.showBottomSheet( data: ActionSheetData( content: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - MenuItem( + const ItemTitle( title: 'ارتباط با سردبیر', - onTap: () {}, icon: DidvanIcons.profile_regular, ), + const SizedBox(height: 16), + for (var i = 0; i < _item.categories.length; i++) ...[ + Padding( + padding: const EdgeInsets.only(right: 20), + child: MenuItem( + titleWidget: DidvanChip(label: _item.categories[i].label), + onTap: () { + Navigator.of(context).pop(); + Navigator.of(context).pushNamed( + Routes.direct, + arguments: _item.categories[i].id, + ); + }, + ), + ), + if (i != _item.categories.length - 1) + const Padding( + padding: EdgeInsets.only(right: 20), + child: DidvanDivider(verticalPadding: 8), + ), + ], const DidvanDivider(), MenuItem( title: 'گزارش اشکال', - onTap: () {}, + onTap: () { + Navigator.of(context).pop(); + Navigator.of(context).pushNamed( + Routes.direct, + arguments: 0, + ); + }, icon: DidvanIcons.description_regular, ), ], diff --git a/lib/widgets/ink_wrapper.dart b/lib/widgets/ink_wrapper.dart index 547709d..f63a044 100644 --- a/lib/widgets/ink_wrapper.dart +++ b/lib/widgets/ink_wrapper.dart @@ -1,3 +1,4 @@ +import 'package:didvan/config/design_config.dart'; import 'package:flutter/material.dart'; class InkWrapper extends StatelessWidget { @@ -13,7 +14,7 @@ class InkWrapper extends StatelessWidget { this.highlightColor, required this.child, this.onPressed, - this.borderRadius, + this.borderRadius = DesignConfig.lowBorderRadius, }) : super(key: key); @override diff --git a/lib/widgets/skeleton_image.dart b/lib/widgets/skeleton_image.dart index eda8f80..c65477b 100644 --- a/lib/widgets/skeleton_image.dart +++ b/lib/widgets/skeleton_image.dart @@ -6,12 +6,10 @@ import 'package:didvan/widgets/shimmer_placeholder.dart'; import 'package:http/http.dart' as http; import 'package:cached_network_image/cached_network_image.dart'; import 'package:didvan/config/design_config.dart'; -import 'package:didvan/config/theme_data.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:skeleton_text/skeleton_text.dart'; class SkeletonImage extends StatefulWidget { final String imageUrl; @@ -110,16 +108,7 @@ class _SkeletonImageState extends State { ), ), progressIndicatorBuilder: (context, url, progress) => - SkeletonAnimation( - shimmerColor: Theme.of(context).colorScheme.border, - borderRadius: widget.borderRadius ?? DesignConfig.lowBorderRadius, - child: Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.disabledBackground, - borderRadius: widget.borderRadius, - ), - ), - ), + const ShimmerPlaceholder(), ); }), ); diff --git a/lib/widgets/tag_item.dart b/lib/widgets/tag_item.dart new file mode 100644 index 0000000..24d9fa3 --- /dev/null +++ b/lib/widgets/tag_item.dart @@ -0,0 +1,44 @@ +import 'package:didvan/config/design_config.dart'; +import 'package:didvan/config/theme_data.dart'; +import 'package:didvan/constants/app_icons.dart'; +import 'package:didvan/widgets/didvan/text.dart'; +import 'package:flutter/material.dart'; + +class TagItem extends StatelessWidget { + final String label; + + const TagItem({ + Key? key, + required this.label, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric( + vertical: 4, + horizontal: 8, + ), + decoration: BoxDecoration( + borderRadius: DesignConfig.lowBorderRadius, + border: Border.all( + color: Theme.of(context).colorScheme.focusedBorder, + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + DidvanIcons.hashtag_regular, + color: Theme.of(context).colorScheme.focusedBorder, + ), + DidvanText( + label, + color: Theme.of(context).colorScheme.focusedBorder, + style: Theme.of(context).textTheme.bodyText1, + ), + ], + ), + ); + } +}