component updates
This commit is contained in:
parent
17f43c3691
commit
0ef35cee2a
|
|
@ -4,15 +4,13 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class BookmarkButton extends StatefulWidget {
|
class BookmarkButton extends StatefulWidget {
|
||||||
final bool value;
|
final bool value;
|
||||||
final VoidCallback onMark;
|
final void Function(bool value) onMarkChanged;
|
||||||
final VoidCallback onUnmark;
|
|
||||||
final bool bigGestureSize;
|
final bool bigGestureSize;
|
||||||
const BookmarkButton({
|
const BookmarkButton({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.value,
|
required this.value,
|
||||||
required this.onMark,
|
|
||||||
required this.onUnmark,
|
|
||||||
this.bigGestureSize = false,
|
this.bigGestureSize = false,
|
||||||
|
required this.onMarkChanged,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -43,7 +41,7 @@ class _BookmarkButtonState extends State<BookmarkButton> {
|
||||||
setState(() {
|
setState(() {
|
||||||
_value = !_value;
|
_value = !_value;
|
||||||
});
|
});
|
||||||
_value ? widget.onMark() : widget.onUnmark();
|
widget.onMarkChanged(_value);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@ class DidvanAppBar extends StatelessWidget {
|
||||||
if (appBarData.subtitle != null)
|
if (appBarData.subtitle != null)
|
||||||
DidvanText(
|
DidvanText(
|
||||||
appBarData.subtitle!,
|
appBarData.subtitle!,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.clip,
|
||||||
style: Theme.of(context).textTheme.overline,
|
style: Theme.of(context).textTheme.overline,
|
||||||
color: Theme.of(context).colorScheme.caption,
|
color: Theme.of(context).colorScheme.caption,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,18 @@ class _DidvanPageViewState extends State<DidvanPageView> {
|
||||||
Widget _contentBuilder(dynamic item, int index) {
|
Widget _contentBuilder(dynamic item, int index) {
|
||||||
final content = item.contents[index];
|
final content = item.contents[index];
|
||||||
if (content.text != null) {
|
if (content.text != null) {
|
||||||
return Html(data: item.contents[index].text!);
|
return Html(
|
||||||
|
data: content.text,
|
||||||
|
style: {
|
||||||
|
'*': Style(
|
||||||
|
direction: TextDirection.rtl,
|
||||||
|
lineHeight: LineHeight.percent(135),
|
||||||
|
// textAlign: TextAlign.justify,
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (content.image != null) {
|
if (content.image != null) {
|
||||||
return SkeletonImage(
|
return SkeletonImage(
|
||||||
|
|
@ -147,11 +158,13 @@ class _DidvanPageViewState extends State<DidvanPageView> {
|
||||||
for (var i = 0; i < item.categories.length; i++)
|
for (var i = 0; i < item.categories.length; i++)
|
||||||
DidvanText(
|
DidvanText(
|
||||||
item.categories[i].label +
|
item.categories[i].label +
|
||||||
' - ' +
|
|
||||||
DateTimeUtils.momentGenerator(item.createdAt) +
|
|
||||||
'${i != item.categories.length - 1 ? '،' : ''} ',
|
'${i != item.categories.length - 1 ? '،' : ''} ',
|
||||||
style: Theme.of(context).textTheme.caption,
|
style: Theme.of(context).textTheme.caption,
|
||||||
),
|
),
|
||||||
|
DidvanText(
|
||||||
|
' - ' + DateTimeUtils.momentGenerator(item.createdAt),
|
||||||
|
style: Theme.of(context).textTheme.caption,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import 'package:didvan/pages/home/settings/widgets/menu_item.dart';
|
import 'package:didvan/pages/home/widgets/menu_item.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ class _DidvanTextFieldState extends State<DidvanTextField> {
|
||||||
enabled: widget.enabled,
|
enabled: widget.enabled,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
hintText: widget.hintText,
|
hintText: widget.hintText,
|
||||||
errorStyle: const TextStyle(height: 0),
|
errorStyle: const TextStyle(height: 0.01),
|
||||||
hintStyle: Theme.of(context)
|
hintStyle: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodyText2!
|
.bodyText2!
|
||||||
|
|
@ -130,7 +130,8 @@ class _DidvanTextFieldState extends State<DidvanTextField> {
|
||||||
color: Theme.of(context).colorScheme.error,
|
color: Theme.of(context).colorScheme.error,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
))
|
),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
import 'package:didvan/config/design_config.dart';
|
import 'package:didvan/config/design_config.dart';
|
||||||
import 'package:didvan/config/theme_data.dart';
|
import 'package:didvan/config/theme_data.dart';
|
||||||
import 'package:didvan/constants/app_icons.dart';
|
import 'package:didvan/constants/app_icons.dart';
|
||||||
import 'package:didvan/models/news_details_data.dart';
|
import 'package:didvan/models/category.dart';
|
||||||
import 'package:didvan/models/radar_details_data.dart';
|
|
||||||
import 'package:didvan/models/view/action_sheet_data.dart';
|
import 'package:didvan/models/view/action_sheet_data.dart';
|
||||||
import 'package:didvan/pages/home/settings/widgets/menu_item.dart';
|
import 'package:didvan/pages/home/widgets/menu_item.dart';
|
||||||
import 'package:didvan/routes/routes.dart';
|
import 'package:didvan/routes/routes.dart';
|
||||||
import 'package:didvan/utils/action_sheet.dart';
|
import 'package:didvan/utils/action_sheet.dart';
|
||||||
import 'package:didvan/widgets/bookmark_button.dart';
|
import 'package:didvan/widgets/bookmark_button.dart';
|
||||||
|
|
@ -16,18 +15,27 @@ import 'package:didvan/widgets/item_title.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class FloatingNavigationBar extends StatefulWidget {
|
class FloatingNavigationBar extends StatefulWidget {
|
||||||
final RadarDetailsData? radar;
|
|
||||||
final NewsDetailsData? news;
|
|
||||||
final ScrollController scrollController;
|
final ScrollController scrollController;
|
||||||
final VoidCallback onMark;
|
final void Function(int count) onCommentsChanged;
|
||||||
final VoidCallback onUnmark;
|
final bool isRadar;
|
||||||
|
final bool marked;
|
||||||
|
final int comments;
|
||||||
|
final int id;
|
||||||
|
final String title;
|
||||||
|
final List<Category>? categories;
|
||||||
|
final void Function(bool value) onMarkChanged;
|
||||||
|
|
||||||
const FloatingNavigationBar({
|
const FloatingNavigationBar({
|
||||||
Key? key,
|
Key? key,
|
||||||
this.radar,
|
|
||||||
this.news,
|
|
||||||
required this.scrollController,
|
required this.scrollController,
|
||||||
required this.onMark,
|
required this.onCommentsChanged,
|
||||||
required this.onUnmark,
|
required this.onMarkChanged,
|
||||||
|
required this.isRadar,
|
||||||
|
required this.marked,
|
||||||
|
required this.comments,
|
||||||
|
required this.id,
|
||||||
|
required this.title,
|
||||||
|
this.categories,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -35,35 +43,13 @@ class FloatingNavigationBar extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
|
class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
|
||||||
bool get _isRadar => widget.radar != null;
|
|
||||||
bool _isScrolled = false;
|
bool _isScrolled = false;
|
||||||
|
int _comments = 0;
|
||||||
get _item => widget.radar ?? widget.news;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didUpdateWidget(covariant FloatingNavigationBar oldWidget) {
|
|
||||||
if (widget.radar != null && oldWidget.radar!.id != widget.radar!.id ||
|
|
||||||
widget.news != null && oldWidget.news!.id != widget.news!.id) {
|
|
||||||
_isScrolled = false;
|
|
||||||
}
|
|
||||||
super.didUpdateWidget(oldWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
widget.scrollController.addListener(() {
|
|
||||||
final position = widget.scrollController.position.pixels;
|
|
||||||
final offset = MediaQuery.of(context).size.width / 16 * 9 + 40;
|
|
||||||
if (position > offset && !_isScrolled) {
|
|
||||||
setState(() {
|
|
||||||
_isScrolled = true;
|
|
||||||
});
|
|
||||||
} else if (position < offset && _isScrolled) {
|
|
||||||
setState(() {
|
|
||||||
_isScrolled = false;
|
_isScrolled = false;
|
||||||
});
|
_comments = widget.comments;
|
||||||
}
|
|
||||||
});
|
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,11 +99,10 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
if (_isRadar)
|
if (widget.isRadar)
|
||||||
BookmarkButton(
|
BookmarkButton(
|
||||||
value: _item.marked,
|
value: widget.marked,
|
||||||
onMark: widget.onMark,
|
onMarkChanged: widget.onMarkChanged,
|
||||||
onUnmark: widget.onUnmark,
|
|
||||||
bigGestureSize: true,
|
bigGestureSize: true,
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
|
|
@ -125,9 +110,9 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
if (_item.comments != 0)
|
if (_comments != 0)
|
||||||
DidvanText(
|
DidvanText(
|
||||||
_item.comments.toString(),
|
_comments.toString(),
|
||||||
color: foregroundColor,
|
color: foregroundColor,
|
||||||
),
|
),
|
||||||
DidvanIconButton(
|
DidvanIconButton(
|
||||||
|
|
@ -135,9 +120,10 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
|
||||||
onPressed: () => Navigator.of(context).pushNamed(
|
onPressed: () => Navigator.of(context).pushNamed(
|
||||||
Routes.comments,
|
Routes.comments,
|
||||||
arguments: {
|
arguments: {
|
||||||
'id': _item.id,
|
'id': widget.id,
|
||||||
'isRadar': _isRadar,
|
'isRadar': widget.isRadar,
|
||||||
'title': _item.title
|
'title': widget.title,
|
||||||
|
'onCommentsChanged': widget.onCommentsChanged,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
icon: DidvanIcons.chats_regular,
|
icon: DidvanIcons.chats_regular,
|
||||||
|
|
@ -145,15 +131,14 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (!_isRadar) const SizedBox(width: 12),
|
if (!widget.isRadar) const SizedBox(width: 12),
|
||||||
if (!_isRadar)
|
if (!widget.isRadar)
|
||||||
BookmarkButton(
|
BookmarkButton(
|
||||||
value: _item.marked,
|
value: widget.marked,
|
||||||
onMark: widget.onMark,
|
onMarkChanged: widget.onMarkChanged,
|
||||||
onUnmark: widget.onUnmark,
|
|
||||||
bigGestureSize: true,
|
bigGestureSize: true,
|
||||||
),
|
),
|
||||||
if (_isRadar)
|
if (widget.isRadar)
|
||||||
DidvanIconButton(
|
DidvanIconButton(
|
||||||
gestureSize: 32,
|
gestureSize: 32,
|
||||||
onPressed: _showMoreOptions,
|
onPressed: _showMoreOptions,
|
||||||
|
|
@ -166,6 +151,7 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showMoreOptions() {
|
void _showMoreOptions() {
|
||||||
|
final categories = widget.categories!;
|
||||||
ActionSheetUtils.showBottomSheet(
|
ActionSheetUtils.showBottomSheet(
|
||||||
data: ActionSheetData(
|
data: ActionSheetData(
|
||||||
content: Column(
|
content: Column(
|
||||||
|
|
@ -176,21 +162,21 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
|
||||||
icon: DidvanIcons.profile_regular,
|
icon: DidvanIcons.profile_regular,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
for (var i = 0; i < _item.categories.length; i++) ...[
|
for (var i = 0; i < categories.length; i++) ...[
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(right: 20),
|
padding: const EdgeInsets.only(right: 20),
|
||||||
child: MenuItem(
|
child: MenuItem(
|
||||||
titleWidget: DidvanChip(label: _item.categories[i].label),
|
titleWidget: DidvanChip(label: categories[i].label),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
Navigator.of(context).pushNamed(
|
Navigator.of(context).pushNamed(
|
||||||
Routes.direct,
|
Routes.direct,
|
||||||
arguments: _item.categories[i].id,
|
arguments: categories[i].id,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (i != _item.categories.length - 1)
|
if (i != categories.length - 1)
|
||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.only(right: 20),
|
padding: EdgeInsets.only(right: 20),
|
||||||
child: DidvanDivider(verticalPadding: 8),
|
child: DidvanDivider(verticalPadding: 8),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,201 @@
|
||||||
|
import 'package:crop/crop.dart';
|
||||||
|
import 'package:didvan/config/theme_data.dart';
|
||||||
|
import 'package:didvan/constants/app_icons.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ImageCropper extends StatefulWidget {
|
||||||
|
final Map<String, dynamic> data;
|
||||||
|
|
||||||
|
const ImageCropper({Key? key, required this.data}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ImageCropper> createState() => _ImageCropperState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ImageCropperState extends State<ImageCropper> {
|
||||||
|
double _rotation = 0;
|
||||||
|
BoxShape shape = BoxShape.rectangle;
|
||||||
|
|
||||||
|
final _controller = CropController();
|
||||||
|
|
||||||
|
get _bytes => widget.data['bytes'];
|
||||||
|
get _onCropped => widget.data['onCropped'];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('برش عکس'),
|
||||||
|
centerTitle: true,
|
||||||
|
actions: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
onPressed: _cropImage,
|
||||||
|
tooltip: 'Crop',
|
||||||
|
icon: const Icon(Icons.crop),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
color: Colors.black,
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: Crop(
|
||||||
|
onChanged: (decomposition) {
|
||||||
|
if (_rotation != decomposition.rotation) {
|
||||||
|
setState(() {
|
||||||
|
_rotation = ((decomposition.rotation + 180) % 360) - 180;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
controller: _controller,
|
||||||
|
shape: shape,
|
||||||
|
child: Image.memory(
|
||||||
|
_bytes,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
helper: shape == BoxShape.rectangle
|
||||||
|
? Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: Colors.white, width: 2),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.redo),
|
||||||
|
tooltip: 'برگرداندن',
|
||||||
|
onPressed: () {
|
||||||
|
_controller.rotation = 0;
|
||||||
|
_controller.scale = 1;
|
||||||
|
_controller.offset = Offset.zero;
|
||||||
|
setState(() {
|
||||||
|
_rotation = 0;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: SliderTheme(
|
||||||
|
data: theme.sliderTheme.copyWith(
|
||||||
|
trackShape: const RectangularSliderTrackShape(),
|
||||||
|
),
|
||||||
|
child: Slider(
|
||||||
|
thumbColor: Theme.of(context).colorScheme.title,
|
||||||
|
divisions: 360,
|
||||||
|
value: _rotation,
|
||||||
|
min: -180,
|
||||||
|
max: 180,
|
||||||
|
label: '$_rotation°',
|
||||||
|
onChanged: (n) {
|
||||||
|
setState(() {
|
||||||
|
_rotation = n.roundToDouble();
|
||||||
|
_controller.rotation = _rotation;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// PopupMenuButton<BoxShape>(
|
||||||
|
// icon: const Icon(Icons.crop_free),
|
||||||
|
// itemBuilder: (context) => [
|
||||||
|
// const PopupMenuItem(
|
||||||
|
// child: Text("Box"),
|
||||||
|
// value: BoxShape.rectangle,
|
||||||
|
// ),
|
||||||
|
// const PopupMenuItem(
|
||||||
|
// child: Text("Oval"),
|
||||||
|
// value: BoxShape.circle,
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// tooltip: 'Crop Shape',
|
||||||
|
// onSelected: (x) {
|
||||||
|
// setState(() {
|
||||||
|
// shape = x;
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// PopupMenuButton<double>(
|
||||||
|
// icon: const Icon(Icons.aspect_ratio),
|
||||||
|
// itemBuilder: (context) => [
|
||||||
|
// const PopupMenuItem(
|
||||||
|
// child: Text("Original"),
|
||||||
|
// value: 1000 / 667.0,
|
||||||
|
// ),
|
||||||
|
// const PopupMenuDivider(),
|
||||||
|
// const PopupMenuItem(
|
||||||
|
// child: Text("16:9"),
|
||||||
|
// value: 16.0 / 9.0,
|
||||||
|
// ),
|
||||||
|
// const PopupMenuItem(
|
||||||
|
// child: Text("4:3"),
|
||||||
|
// value: 4.0 / 3.0,
|
||||||
|
// ),
|
||||||
|
// const PopupMenuItem(
|
||||||
|
// child: Text("1:1"),
|
||||||
|
// value: 1,
|
||||||
|
// ),
|
||||||
|
// const PopupMenuItem(
|
||||||
|
// child: Text("3:4"),
|
||||||
|
// value: 3.0 / 4.0,
|
||||||
|
// ),
|
||||||
|
// const PopupMenuItem(
|
||||||
|
// child: Text("9:16"),
|
||||||
|
// value: 9.0 / 16.0,
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// tooltip: 'Aspect Ratio',
|
||||||
|
// onSelected: (x) {
|
||||||
|
// _controller.aspectRatio = x;
|
||||||
|
// setState(() {});
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cropImage() async {
|
||||||
|
final pixelRatio = MediaQuery.of(context).devicePixelRatio;
|
||||||
|
final cropped = await _controller.crop(pixelRatio: pixelRatio);
|
||||||
|
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('تایید برش'),
|
||||||
|
centerTitle: true,
|
||||||
|
actions: [
|
||||||
|
Builder(
|
||||||
|
builder: (context) => IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
DidvanIcons.check_circle_solid,
|
||||||
|
size: 32,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
_onCropped();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: Center(
|
||||||
|
child: RawImage(
|
||||||
|
image: cropped,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
fullscreenDialog: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
import 'package:didvan/config/theme_data.dart';
|
||||||
|
import 'package:didvan/constants/app_icons.dart';
|
||||||
|
import 'package:didvan/models/item_overview.dart';
|
||||||
|
import 'package:didvan/widgets/didvan/card.dart';
|
||||||
|
import 'package:didvan/widgets/didvan/text.dart';
|
||||||
|
import 'package:didvan/widgets/skeleton_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:persian_number_utility/persian_number_utility.dart';
|
||||||
|
|
||||||
|
class MultitypeItem extends StatelessWidget {
|
||||||
|
final ItemOverview item;
|
||||||
|
const MultitypeItem({Key? key, required this.item}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return DidvanCard(
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
|
SkeletonImage(imageUrl: item.image, height: 80, width: 80),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
borderRadius: const BorderRadius.horizontal(
|
||||||
|
left: Radius.circular(10),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
item.type == 'radar'
|
||||||
|
? DidvanIcons.radar_light
|
||||||
|
: DidvanIcons.news_light,
|
||||||
|
color: Theme.of(context).colorScheme.white,
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: SizedBox(
|
||||||
|
height: 80,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
DidvanText(
|
||||||
|
item.title,
|
||||||
|
style: Theme.of(context).textTheme.bodyText1,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
DidvanIcons.calendar_day_light,
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
DidvanText(
|
||||||
|
DateTime.parse(item.createdAt).toPersianDateStr(),
|
||||||
|
style: Theme.of(context).textTheme.overline,
|
||||||
|
),
|
||||||
|
// DidvanText('text'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:didvan/services/storage/storage.dart';
|
|
||||||
import 'package:didvan/widgets/shimmer_placeholder.dart';
|
import 'package:didvan/widgets/shimmer_placeholder.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
|
@ -31,8 +29,8 @@ class SkeletonImage extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SkeletonImageState extends State<SkeletonImage> {
|
class _SkeletonImageState extends State<SkeletonImage> {
|
||||||
Uint8List? _bytes;
|
late Uint8List _bytes;
|
||||||
|
bool _isLoading = true;
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
if (kIsWeb) _getImage();
|
if (kIsWeb) _getImage();
|
||||||
|
|
@ -41,57 +39,76 @@ class _SkeletonImageState extends State<SkeletonImage> {
|
||||||
|
|
||||||
Future<void> _getImage() async {
|
Future<void> _getImage() async {
|
||||||
final url = RequestHelper.baseUrl + widget.imageUrl;
|
final url = RequestHelper.baseUrl + widget.imageUrl;
|
||||||
final storage = StorageService.webStorage;
|
|
||||||
String? imageCache = storage['image-cache'];
|
// final storage = StorageService.webStorage;
|
||||||
final Map data = imageCache == null ? {} : jsonDecode(imageCache);
|
// String? imageCache = storage['image-cache'];
|
||||||
if (data.containsKey(url)) {
|
// final Map data = imageCache == null ? {} : jsonDecode(imageCache);
|
||||||
_bytes = Uint8List.fromList(
|
// if (data.containsKey(url)) {
|
||||||
List<int>.from(data[url]),
|
// _bytes = Uint8List.fromList(
|
||||||
);
|
// List<int>.from(data[url]),
|
||||||
} else {
|
// );
|
||||||
|
// } else {
|
||||||
_bytes = (await http.get(
|
_bytes = (await http.get(
|
||||||
Uri.parse(url),
|
Uri.parse(url),
|
||||||
headers: {'Authorization': 'Bearer ${RequestService.token}'},
|
headers: {'Authorization': 'Bearer ${RequestService.token}'},
|
||||||
))
|
))
|
||||||
.bodyBytes;
|
.bodyBytes;
|
||||||
addImageToStorage();
|
// addImageToStorage();
|
||||||
}
|
// }
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {});
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void addImageToStorage() {
|
// void addImageToStorage() {
|
||||||
final storage = StorageService.webStorage;
|
// final storage = StorageService.webStorage;
|
||||||
String? imageCache = storage['image-cache'];
|
// String? imageCache = storage['image-cache'];
|
||||||
final Map data = imageCache == null ? {} : Map.from(jsonDecode(imageCache));
|
// final Map data = imageCache == null ? {} : Map.from(jsonDecode(imageCache));
|
||||||
data.addAll({RequestHelper.baseUrl + widget.imageUrl: _bytes});
|
// data.addAll({RequestHelper.baseUrl + widget.imageUrl: _bytes});
|
||||||
StorageService.webStorage.addAll({'image-cache': jsonEncode(data)});
|
// StorageService.webStorage.addAll({'image-cache': jsonEncode(data)});
|
||||||
}
|
// }
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return _aspectRatioGenerator(
|
return _aspectRatioGenerator(
|
||||||
child: Builder(builder: (context) {
|
child: Builder(builder: (context) {
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
return Builder(
|
if (_isLoading) {
|
||||||
builder: (context) {
|
|
||||||
if (_bytes == null || _bytes!.isEmpty) {
|
|
||||||
return ShimmerPlaceholder(
|
return ShimmerPlaceholder(
|
||||||
borderRadius: widget.borderRadius,
|
borderRadius: widget.borderRadius,
|
||||||
|
width: widget.aspectRatio == null ? widget.width : null,
|
||||||
|
height: widget.aspectRatio == null ? widget.height : null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return ClipRRect(
|
return ClipRRect(
|
||||||
borderRadius: widget.borderRadius,
|
borderRadius: widget.borderRadius,
|
||||||
child: Image.memory(
|
child: Image.memory(
|
||||||
_bytes!,
|
_bytes,
|
||||||
|
fit: BoxFit.cover,
|
||||||
width: widget.width,
|
width: widget.width,
|
||||||
height: widget.height,
|
height: widget.height,
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
// Builder(
|
||||||
);
|
// builder: (context) {
|
||||||
|
// if (_bytes == null || _bytes!.isEmpty) {
|
||||||
|
// return ShimmerPlaceholder(
|
||||||
|
// borderRadius: widget.borderRadius,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// return ClipRRect(
|
||||||
|
// borderRadius: widget.borderRadius,
|
||||||
|
// child: Image.memory(
|
||||||
|
// _bytes!,
|
||||||
|
// width: widget.width,
|
||||||
|
// height: widget.height,
|
||||||
|
// fit: BoxFit.cover,
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// );
|
||||||
}
|
}
|
||||||
return CachedNetworkImage(
|
return CachedNetworkImage(
|
||||||
httpHeaders: {'Authorization': 'Bearer ${RequestService.token}'},
|
httpHeaders: {'Authorization': 'Bearer ${RequestService.token}'},
|
||||||
|
|
@ -108,7 +125,9 @@ class _SkeletonImageState extends State<SkeletonImage> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
progressIndicatorBuilder: (context, url, progress) =>
|
progressIndicatorBuilder: (context, url, progress) =>
|
||||||
const ShimmerPlaceholder(),
|
ShimmerPlaceholder(
|
||||||
|
borderRadius: widget.borderRadius,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import 'package:didvan/constants/assets.dart';
|
||||||
|
import 'package:didvan/widgets/state_handlers/empty_state.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class EmptyList extends StatelessWidget {
|
||||||
|
const EmptyList({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return EmptyState(
|
||||||
|
asset: Assets.emptyBookmark,
|
||||||
|
title: 'لیست خالی است',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,7 @@ class SliverStateHandler<T extends CoreProvier> extends SliverList {
|
||||||
final Widget? emptyState;
|
final Widget? emptyState;
|
||||||
final Widget? placeholder;
|
final Widget? placeholder;
|
||||||
final EdgeInsets? itemPadding;
|
final EdgeInsets? itemPadding;
|
||||||
|
final bool centerEmptyState;
|
||||||
SliverStateHandler({
|
SliverStateHandler({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.state,
|
required this.state,
|
||||||
|
|
@ -22,21 +23,26 @@ class SliverStateHandler<T extends CoreProvier> extends SliverList {
|
||||||
this.placeholder,
|
this.placeholder,
|
||||||
this.emptyState,
|
this.emptyState,
|
||||||
this.enableEmptyState = false,
|
this.enableEmptyState = false,
|
||||||
|
this.centerEmptyState = true,
|
||||||
}) : super(
|
}) : super(
|
||||||
key: key,
|
key: key,
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
if (state.appState == AppState.failed) {
|
if (state.appState == AppState.failed) {
|
||||||
return SizedBox(
|
return Padding(
|
||||||
height: MediaQuery.of(context).size.height - 240,
|
padding: EdgeInsets.only(
|
||||||
child: EmptyConnection(
|
top: centerEmptyState ? 120 : 20,
|
||||||
onRetry: onRetry,
|
bottom: 20,
|
||||||
),
|
),
|
||||||
|
child: EmptyConnection(onRetry: onRetry),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (enableEmptyState && state.appState == AppState.idle) {
|
if (enableEmptyState && state.appState == AppState.idle) {
|
||||||
return SizedBox(
|
return Padding(
|
||||||
height: MediaQuery.of(context).size.height - 240,
|
padding: EdgeInsets.only(
|
||||||
|
top: centerEmptyState ? 120 : 20,
|
||||||
|
bottom: 20,
|
||||||
|
),
|
||||||
child: emptyState,
|
child: emptyState,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue