D1APP-47 empty states configuration
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
|
@ -5,6 +5,7 @@ class Assets {
|
||||||
static const String _basePath = 'lib/assets';
|
static const String _basePath = 'lib/assets';
|
||||||
static const String _baseImagesPath = _basePath + '/images';
|
static const String _baseImagesPath = _basePath + '/images';
|
||||||
static const String _baseThemesPath = _basePath + '/images/themes';
|
static const String _baseThemesPath = _basePath + '/images/themes';
|
||||||
|
static const String _baseEmptyStatesPath = _basePath + '/images/empty_states';
|
||||||
static const String _baseAnimationsPath = _basePath + '/animations';
|
static const String _baseAnimationsPath = _basePath + '/animations';
|
||||||
static const String _baseRecordsPath = _basePath + '/images/records';
|
static const String _baseRecordsPath = _basePath + '/images/records';
|
||||||
|
|
||||||
|
|
@ -29,6 +30,19 @@ class Assets {
|
||||||
static String get techCategoryIcon =>
|
static String get techCategoryIcon =>
|
||||||
_baseImagesPath + '/categories/tech-$_themeSuffix.svg';
|
_baseImagesPath + '/categories/tech-$_themeSuffix.svg';
|
||||||
|
|
||||||
|
static String get emptyBookmark =>
|
||||||
|
_baseEmptyStatesPath + '/bookmark-$_themeSuffix.svg';
|
||||||
|
static String get emptyChart =>
|
||||||
|
_baseEmptyStatesPath + '/chart-$_themeSuffix.svg';
|
||||||
|
static String get emptyChat =>
|
||||||
|
_baseEmptyStatesPath + '/chat-$_themeSuffix.svg';
|
||||||
|
static String get emptyConnection =>
|
||||||
|
_baseEmptyStatesPath + '/connection-$_themeSuffix.svg';
|
||||||
|
static String get emptyResult =>
|
||||||
|
_baseEmptyStatesPath + '/result-$_themeSuffix.svg';
|
||||||
|
static String get emptyStudio =>
|
||||||
|
_baseEmptyStatesPath + '/studio-$_themeSuffix.svg';
|
||||||
|
|
||||||
static const String lightTheme = _baseThemesPath + '/theme-light.svg';
|
static const String lightTheme = _baseThemesPath + '/theme-light.svg';
|
||||||
static const String darkTheme = _baseThemesPath + '/theme-dark.svg';
|
static const String darkTheme = _baseThemesPath + '/theme-dark.svg';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:didvan/constants/app_icons.dart';
|
import 'package:didvan/constants/app_icons.dart';
|
||||||
|
import 'package:didvan/models/enums.dart';
|
||||||
import 'package:didvan/models/view/action_sheet_data.dart';
|
import 'package:didvan/models/view/action_sheet_data.dart';
|
||||||
import 'package:didvan/pages/home/news/news_state.dart';
|
import 'package:didvan/pages/home/news/news_state.dart';
|
||||||
import 'package:didvan/pages/home/news/widgets/news_item.dart';
|
import 'package:didvan/pages/home/news/widgets/news_item.dart';
|
||||||
|
|
@ -12,7 +13,8 @@ import 'package:didvan/pages/home/widgets/logo_app_bar.dart';
|
||||||
import 'package:didvan/widgets/didvan/card.dart';
|
import 'package:didvan/widgets/didvan/card.dart';
|
||||||
import 'package:didvan/widgets/didvan/divider.dart';
|
import 'package:didvan/widgets/didvan/divider.dart';
|
||||||
import 'package:didvan/widgets/shimmer_placeholder.dart';
|
import 'package:didvan/widgets/shimmer_placeholder.dart';
|
||||||
import 'package:didvan/widgets/sliver_state_handler.dart';
|
import 'package:didvan/widgets/state_handlers/empty_result.dart';
|
||||||
|
import 'package:didvan/widgets/state_handlers/sliver_state_handler.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
|
@ -25,25 +27,25 @@ class News extends StatefulWidget {
|
||||||
|
|
||||||
class _NewsState extends State<News> {
|
class _NewsState extends State<News> {
|
||||||
Timer? _timer;
|
Timer? _timer;
|
||||||
|
final _focusNode = FocusNode();
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
Future.delayed(Duration.zero, () {
|
context.read<NewsState>().init();
|
||||||
context.read<NewsState>().getNews(page: 1);
|
|
||||||
});
|
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final state = context.watch<NewsState>();
|
final state = context.watch<NewsState>();
|
||||||
return Scaffold(
|
return CustomScrollView(
|
||||||
body: CustomScrollView(
|
slivers: [
|
||||||
slivers: [
|
const SliverToBoxAdapter(child: LogoAppBar()),
|
||||||
const SliverToBoxAdapter(child: LogoAppBar()),
|
if (state.appState != AppState.failed)
|
||||||
SliverPadding(
|
SliverPadding(
|
||||||
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16),
|
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16),
|
||||||
sliver: SliverToBoxAdapter(
|
sliver: SliverToBoxAdapter(
|
||||||
child: SearchField(
|
child: SearchField(
|
||||||
|
focusNode: _focusNode,
|
||||||
title: 'اخبار',
|
title: 'اخبار',
|
||||||
onChanged: _onChanged,
|
onChanged: _onChanged,
|
||||||
onFilterButtonPressed: _showFilterBottomSheet,
|
onFilterButtonPressed: _showFilterBottomSheet,
|
||||||
|
|
@ -51,17 +53,21 @@ class _NewsState extends State<News> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SliverStateHandler<NewsState>(
|
SliverStateHandler<NewsState>(
|
||||||
state: state,
|
onRetry: () => state.getNews(page: state.page),
|
||||||
builder: (context, state, index) => NewsItem(
|
state: state,
|
||||||
news: state.news[index],
|
builder: (context, state, index) => NewsItem(
|
||||||
),
|
news: state.news[index],
|
||||||
childCount: state.news.length,
|
|
||||||
itemPadding: const EdgeInsets.only(left: 16, right: 16, bottom: 16),
|
|
||||||
placeholder: const _NewsItemPlaceholder(),
|
|
||||||
),
|
),
|
||||||
],
|
enableEmptyState: state.news.isEmpty,
|
||||||
),
|
emptyState: EmptyResult(
|
||||||
|
onNewSearch: () => _focusNode.requestFocus(),
|
||||||
|
),
|
||||||
|
childCount: state.news.length,
|
||||||
|
itemPadding: const EdgeInsets.only(left: 16, right: 16, bottom: 16),
|
||||||
|
placeholder: const _NewsItemPlaceholder(),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,8 @@ import 'package:didvan/widgets/didvan/divider.dart';
|
||||||
import 'package:didvan/widgets/didvan/text.dart';
|
import 'package:didvan/widgets/didvan/text.dart';
|
||||||
import 'package:didvan/widgets/item_title.dart';
|
import 'package:didvan/widgets/item_title.dart';
|
||||||
import 'package:didvan/widgets/shimmer_placeholder.dart';
|
import 'package:didvan/widgets/shimmer_placeholder.dart';
|
||||||
import 'package:didvan/widgets/sliver_state_handler.dart';
|
import 'package:didvan/widgets/state_handlers/empty_result.dart';
|
||||||
|
import 'package:didvan/widgets/state_handlers/sliver_state_handler.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
|
@ -34,7 +35,7 @@ class Radar extends StatefulWidget {
|
||||||
|
|
||||||
class _RadarState extends State<Radar> {
|
class _RadarState extends State<Radar> {
|
||||||
final ScrollController _scrollController = ScrollController();
|
final ScrollController _scrollController = ScrollController();
|
||||||
// final ScrollController _categoriesScrollController = ScrollController();
|
final _focusNode = FocusNode();
|
||||||
|
|
||||||
bool _isAnimating = false;
|
bool _isAnimating = false;
|
||||||
|
|
||||||
|
|
@ -58,18 +59,18 @@ class _RadarState extends State<Radar> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Consumer<RadarState>(
|
||||||
body: Consumer<RadarState>(
|
builder: (context, state, child) => Stack(
|
||||||
builder: (context, state, child) => Stack(
|
children: [
|
||||||
children: [
|
CustomScrollView(
|
||||||
CustomScrollView(
|
physics: _isAnimating ||
|
||||||
physics: _isAnimating ||
|
(state.appState == AppState.busy && state.radars.isEmpty)
|
||||||
(state.appState == AppState.busy && state.radars.isEmpty)
|
? const NeverScrollableScrollPhysics()
|
||||||
? const NeverScrollableScrollPhysics()
|
: const ScrollPhysics(),
|
||||||
: const ScrollPhysics(),
|
controller: _scrollController,
|
||||||
controller: _scrollController,
|
slivers: [
|
||||||
slivers: [
|
const SliverToBoxAdapter(child: LogoAppBar()),
|
||||||
const SliverToBoxAdapter(child: LogoAppBar()),
|
if (state.appState != AppState.failed)
|
||||||
SliverPadding(
|
SliverPadding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
left: 16,
|
left: 16,
|
||||||
|
|
@ -78,6 +79,7 @@ class _RadarState extends State<Radar> {
|
||||||
),
|
),
|
||||||
sliver: SliverToBoxAdapter(
|
sliver: SliverToBoxAdapter(
|
||||||
child: SearchField(
|
child: SearchField(
|
||||||
|
focusNode: _focusNode,
|
||||||
isFiltered: state.filtering,
|
isFiltered: state.filtering,
|
||||||
title: 'رادار',
|
title: 'رادار',
|
||||||
onChanged: _onChanged,
|
onChanged: _onChanged,
|
||||||
|
|
@ -85,10 +87,13 @@ class _RadarState extends State<Radar> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (!state.filtering && !state.searching)
|
if (!state.filtering &&
|
||||||
const SliverToBoxAdapter(
|
!state.searching &&
|
||||||
child: SizedBox(height: 276),
|
state.appState != AppState.failed)
|
||||||
),
|
const SliverToBoxAdapter(
|
||||||
|
child: SizedBox(height: 276),
|
||||||
|
),
|
||||||
|
if (state.appState != AppState.failed)
|
||||||
SliverPadding(
|
SliverPadding(
|
||||||
padding: const EdgeInsets.only(right: 16, bottom: 20),
|
padding: const EdgeInsets.only(right: 16, bottom: 20),
|
||||||
sliver: SliverToBoxAdapter(
|
sliver: SliverToBoxAdapter(
|
||||||
|
|
@ -108,38 +113,44 @@ class _RadarState extends State<Radar> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SliverStateHandler<RadarState>(
|
SliverStateHandler<RadarState>(
|
||||||
state: state,
|
onRetry: () => state.getRadarOverviews(page: state.page),
|
||||||
itemPadding: const EdgeInsets.only(
|
state: state,
|
||||||
bottom: 20,
|
itemPadding: const EdgeInsets.only(
|
||||||
left: 16,
|
bottom: 20,
|
||||||
right: 16,
|
left: 16,
|
||||||
),
|
right: 16,
|
||||||
placeholder: const _RadarItemPlaceholder(),
|
|
||||||
builder: (context, state, index) => RadarItem(
|
|
||||||
radar: state.radars[index],
|
|
||||||
),
|
|
||||||
childCount: state.radars.length,
|
|
||||||
),
|
),
|
||||||
if (state.radars.length == 1)
|
enableEmptyState: state.radars.isEmpty,
|
||||||
const SliverToBoxAdapter(
|
emptyState:
|
||||||
child: SizedBox(height: 320),
|
EmptyResult(onNewSearch: () => _focusNode.requestFocus()),
|
||||||
),
|
placeholder: const _RadarItemPlaceholder(),
|
||||||
],
|
builder: (context, state, index) => RadarItem(
|
||||||
),
|
radar: state.radars[index],
|
||||||
|
),
|
||||||
|
childCount: state.radars.length,
|
||||||
|
),
|
||||||
|
if (state.radars.length == 1)
|
||||||
|
const SliverToBoxAdapter(
|
||||||
|
child: SizedBox(height: 320),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (state.appState != AppState.failed)
|
||||||
CategoriesRow1(
|
CategoriesRow1(
|
||||||
isColapsed:
|
isColapsed:
|
||||||
state.isColapsed || state.searching || state.filtering,
|
state.isColapsed || state.searching || state.filtering,
|
||||||
),
|
),
|
||||||
|
if (state.appState != AppState.failed)
|
||||||
CategoriesRow2(
|
CategoriesRow2(
|
||||||
isColapsed:
|
isColapsed:
|
||||||
state.isColapsed || state.searching || state.filtering,
|
state.isColapsed || state.searching || state.filtering,
|
||||||
),
|
),
|
||||||
|
if (state.appState != AppState.failed)
|
||||||
CategoriesList(
|
CategoriesList(
|
||||||
isColapsed: state.isColapsed && !state.searching,
|
isColapsed: state.isColapsed && !state.searching,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:didvan/constants/assets.dart';
|
import 'package:didvan/constants/assets.dart';
|
||||||
import 'package:didvan/models/enums.dart';
|
import 'package:didvan/models/enums.dart';
|
||||||
|
import 'package:didvan/models/requests/radar.dart';
|
||||||
import 'package:didvan/models/view/radar_category.dart';
|
import 'package:didvan/models/view/radar_category.dart';
|
||||||
import 'package:didvan/models/radar_overview.dart';
|
import 'package:didvan/models/radar_overview.dart';
|
||||||
import 'package:didvan/providers/core_provider.dart';
|
import 'package:didvan/providers/core_provider.dart';
|
||||||
|
|
@ -12,6 +13,7 @@ class RadarState extends CoreProvier {
|
||||||
String lastSearch = '';
|
String lastSearch = '';
|
||||||
String? startDate;
|
String? startDate;
|
||||||
String? endDate;
|
String? endDate;
|
||||||
|
int page = 1;
|
||||||
bool isScrolled = false;
|
bool isScrolled = false;
|
||||||
bool shouldColapse = false;
|
bool shouldColapse = false;
|
||||||
final List<MapEntry> _markQueue = [];
|
final List<MapEntry> _markQueue = [];
|
||||||
|
|
@ -44,16 +46,21 @@ class RadarState extends CoreProvier {
|
||||||
Future<void> getRadarOverviews({
|
Future<void> getRadarOverviews({
|
||||||
required int page,
|
required int page,
|
||||||
}) async {
|
}) async {
|
||||||
radars.clear();
|
if (this.page == page) {
|
||||||
|
radars.clear();
|
||||||
|
}
|
||||||
|
this.page = page;
|
||||||
lastSearch = search;
|
lastSearch = search;
|
||||||
appState = AppState.busy;
|
appState = AppState.busy;
|
||||||
final RequestService service = RequestService(
|
final RequestService service = RequestService(
|
||||||
RequestHelper.radarOverviews(
|
RequestHelper.radarOverviews(
|
||||||
page: page,
|
args: RadarRequestArgs(
|
||||||
startDate: startDate?.split(' ').first,
|
page: page,
|
||||||
endDate: endDate?.split(' ').first,
|
startDate: startDate?.split(' ').first,
|
||||||
search: search == '' ? null : search,
|
endDate: endDate?.split(' ').first,
|
||||||
categories: selectedCats.map((e) => e.id).toList(),
|
search: search == '' ? null : search,
|
||||||
|
categories: selectedCats.map((e) => e.id).toList(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
await service.httpGet();
|
await service.httpGet();
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import 'package:didvan/widgets/didvan/divider.dart';
|
||||||
import 'package:didvan/widgets/didvan/scaffold.dart';
|
import 'package:didvan/widgets/didvan/scaffold.dart';
|
||||||
import 'package:didvan/widgets/didvan/text.dart';
|
import 'package:didvan/widgets/didvan/text.dart';
|
||||||
import 'package:didvan/widgets/item_title.dart';
|
import 'package:didvan/widgets/item_title.dart';
|
||||||
import 'package:didvan/widgets/state_handler.dart';
|
import 'package:didvan/widgets/state_handlers/state_handler.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
@ -27,6 +27,7 @@ class GeneralSettings extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Consumer<GeneralSettingsState>(
|
return Consumer<GeneralSettingsState>(
|
||||||
builder: (context, state, child) => StateHandler<GeneralSettingsState>(
|
builder: (context, state, child) => StateHandler<GeneralSettingsState>(
|
||||||
|
onRetry: () {},
|
||||||
state: context.read<GeneralSettingsState>(),
|
state: context.read<GeneralSettingsState>(),
|
||||||
builder: (context, state) => DidvanScaffold(
|
builder: (context, state) => DidvanScaffold(
|
||||||
appBarData: AppBarData(hasBack: true, title: 'تنظیمات'),
|
appBarData: AppBarData(hasBack: true, title: 'تنظیمات'),
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
import 'package:didvan/config/theme_data.dart';
|
||||||
|
import 'package:didvan/constants/assets.dart';
|
||||||
|
import 'package:didvan/pages/home/widgets/logo_app_bar.dart';
|
||||||
|
import 'package:didvan/widgets/state_handlers/empty_state.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class Statictics extends StatelessWidget {
|
class Statictics extends StatelessWidget {
|
||||||
|
|
@ -5,6 +9,18 @@ class Statictics extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container();
|
return Column(
|
||||||
|
children: [
|
||||||
|
const LogoAppBar(),
|
||||||
|
Expanded(
|
||||||
|
child: EmptyState(
|
||||||
|
asset: Assets.emptyChart,
|
||||||
|
title: 'قیمتها و شاخصهای اقتصادی',
|
||||||
|
subtitle: 'به زودی...',
|
||||||
|
titleColor: Theme.of(context).colorScheme.title,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
import 'package:didvan/config/theme_data.dart';
|
||||||
|
import 'package:didvan/constants/assets.dart';
|
||||||
|
import 'package:didvan/pages/home/widgets/logo_app_bar.dart';
|
||||||
|
import 'package:didvan/widgets/state_handlers/empty_state.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class Studio extends StatelessWidget {
|
class Studio extends StatelessWidget {
|
||||||
|
|
@ -5,6 +9,18 @@ class Studio extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container();
|
return Column(
|
||||||
|
children: [
|
||||||
|
const LogoAppBar(),
|
||||||
|
Expanded(
|
||||||
|
child: EmptyState(
|
||||||
|
asset: Assets.emptyStudio,
|
||||||
|
title: 'استودیو آینده',
|
||||||
|
subtitle: 'به زودی...',
|
||||||
|
titleColor: Theme.of(context).colorScheme.title,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class SearchField extends StatefulWidget {
|
class SearchField extends StatefulWidget {
|
||||||
final String title;
|
final String title;
|
||||||
|
final FocusNode focusNode;
|
||||||
final bool? isFiltered;
|
final bool? isFiltered;
|
||||||
final void Function(String value) onChanged;
|
final void Function(String value) onChanged;
|
||||||
final VoidCallback? onFilterButtonPressed;
|
final VoidCallback? onFilterButtonPressed;
|
||||||
|
|
@ -13,6 +14,7 @@ class SearchField extends StatefulWidget {
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.onChanged,
|
required this.onChanged,
|
||||||
|
required this.focusNode,
|
||||||
this.onFilterButtonPressed,
|
this.onFilterButtonPressed,
|
||||||
this.isFiltered,
|
this.isFiltered,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
@ -22,11 +24,9 @@ class SearchField extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SearchFieldState extends State<SearchField> {
|
class _SearchFieldState extends State<SearchField> {
|
||||||
final FocusNode _focusNode = FocusNode();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
_focusNode.addListener(() {
|
widget.focusNode.addListener(() {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
});
|
});
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
@ -44,8 +44,8 @@ class _SearchFieldState extends State<SearchField> {
|
||||||
color: _fillColor(),
|
color: _fillColor(),
|
||||||
),
|
),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
focusNode: _focusNode,
|
focusNode: widget.focusNode,
|
||||||
style: Theme.of(context).textTheme.bodyText1,
|
style: Theme.of(context).textTheme.bodyText2,
|
||||||
textAlignVertical: TextAlignVertical.center,
|
textAlignVertical: TextAlignVertical.center,
|
||||||
onChanged: widget.onChanged,
|
onChanged: widget.onChanged,
|
||||||
keyboardType: TextInputType.text,
|
keyboardType: TextInputType.text,
|
||||||
|
|
@ -117,7 +117,7 @@ class _SearchFieldState extends State<SearchField> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Color _fillColor() {
|
Color _fillColor() {
|
||||||
if (_focusNode.hasFocus) {
|
if (widget.focusNode.hasFocus) {
|
||||||
return Theme.of(context).colorScheme.surface;
|
return Theme.of(context).colorScheme.surface;
|
||||||
}
|
}
|
||||||
return Theme.of(context).colorScheme.surface;
|
return Theme.of(context).colorScheme.surface;
|
||||||
|
|
@ -125,7 +125,7 @@ class _SearchFieldState extends State<SearchField> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_focusNode.dispose();
|
widget.focusNode.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
import 'package:didvan/constants/assets.dart';
|
||||||
|
import 'package:didvan/widgets/state_handlers/empty_state.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class EmptyConnection extends StatelessWidget {
|
||||||
|
final VoidCallback onRetry;
|
||||||
|
const EmptyConnection({Key? key, required this.onRetry}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return EmptyState(
|
||||||
|
asset: Assets.emptyConnection,
|
||||||
|
title: 'ارتباط با اینترنت قطع شد...',
|
||||||
|
action: onRetry,
|
||||||
|
buttonTitle: 'تلاش دوباره',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
import 'package:didvan/constants/assets.dart';
|
||||||
|
import 'package:didvan/widgets/state_handlers/empty_state.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class EmptyResult extends StatelessWidget {
|
||||||
|
final VoidCallback onNewSearch;
|
||||||
|
const EmptyResult({Key? key, required this.onNewSearch}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return EmptyState(
|
||||||
|
asset: Assets.emptyResult,
|
||||||
|
title: 'نتیجهای پیدا نشد',
|
||||||
|
buttonTitle: 'تغییر جستجو',
|
||||||
|
action: onNewSearch,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
import 'package:didvan/config/theme_data.dart';
|
||||||
|
import 'package:didvan/widgets/didvan/button.dart';
|
||||||
|
import 'package:didvan/widgets/didvan/text.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
|
||||||
|
class EmptyState extends StatelessWidget {
|
||||||
|
final String asset;
|
||||||
|
final String title;
|
||||||
|
final String? subtitle;
|
||||||
|
final String? buttonTitle;
|
||||||
|
final VoidCallback? action;
|
||||||
|
final Color? titleColor;
|
||||||
|
|
||||||
|
const EmptyState({
|
||||||
|
Key? key,
|
||||||
|
required this.asset,
|
||||||
|
required this.title,
|
||||||
|
this.action,
|
||||||
|
this.buttonTitle,
|
||||||
|
this.subtitle,
|
||||||
|
this.titleColor,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
SizedBox(height: 210, child: SvgPicture.asset(asset)),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
DidvanText(
|
||||||
|
title,
|
||||||
|
style: Theme.of(context).textTheme.headline3,
|
||||||
|
color: titleColor ?? Theme.of(context).colorScheme.caption,
|
||||||
|
),
|
||||||
|
if (subtitle != null) const SizedBox(height: 8),
|
||||||
|
if (subtitle != null)
|
||||||
|
DidvanText(
|
||||||
|
subtitle!,
|
||||||
|
color: Theme.of(context).colorScheme.caption,
|
||||||
|
),
|
||||||
|
if (action != null) const SizedBox(height: 16),
|
||||||
|
if (action != null)
|
||||||
|
DidvanButton(
|
||||||
|
height: 40,
|
||||||
|
onPressed: action,
|
||||||
|
title: buttonTitle,
|
||||||
|
width: 112,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import 'package:didvan/models/enums.dart';
|
import 'package:didvan/models/enums.dart';
|
||||||
import 'package:didvan/providers/core_provider.dart';
|
import 'package:didvan/providers/core_provider.dart';
|
||||||
import 'package:didvan/widgets/didvan/text.dart';
|
import 'package:didvan/widgets/state_handlers/empty_connection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class SliverStateHandler<T extends CoreProvier> extends SliverList {
|
class SliverStateHandler<T extends CoreProvier> extends SliverList {
|
||||||
final T state;
|
final T state;
|
||||||
final Widget Function(BuildContext context, T state, int index) builder;
|
final Widget Function(BuildContext context, T state, int index) builder;
|
||||||
final int childCount;
|
final int childCount;
|
||||||
final VoidCallback? onRefresh;
|
final VoidCallback onRetry;
|
||||||
final bool enableEmptyState;
|
final bool enableEmptyState;
|
||||||
final Widget? emptyState;
|
final Widget? emptyState;
|
||||||
final Widget? placeholder;
|
final Widget? placeholder;
|
||||||
|
|
@ -17,20 +17,28 @@ class SliverStateHandler<T extends CoreProvier> extends SliverList {
|
||||||
required this.state,
|
required this.state,
|
||||||
required this.builder,
|
required this.builder,
|
||||||
required this.childCount,
|
required this.childCount,
|
||||||
|
required this.onRetry,
|
||||||
this.itemPadding,
|
this.itemPadding,
|
||||||
this.placeholder,
|
this.placeholder,
|
||||||
this.emptyState,
|
this.emptyState,
|
||||||
this.enableEmptyState = false,
|
this.enableEmptyState = false,
|
||||||
this.onRefresh,
|
|
||||||
}) : 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 const DidvanText('مشکل اتصال');
|
return SizedBox(
|
||||||
|
height: MediaQuery.of(context).size.height - 240,
|
||||||
|
child: EmptyConnection(
|
||||||
|
onRetry: onRetry,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (enableEmptyState) {
|
if (enableEmptyState && state.appState == AppState.idle) {
|
||||||
return emptyState;
|
return SizedBox(
|
||||||
|
height: MediaQuery.of(context).size.height - 240,
|
||||||
|
child: emptyState,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (state.appState == AppState.busy) {
|
if (state.appState == AppState.busy) {
|
||||||
return Padding(
|
return Padding(
|
||||||
|
|
@ -44,7 +52,9 @@ class SliverStateHandler<T extends CoreProvier> extends SliverList {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
childCount: state.appState == AppState.idle
|
childCount: state.appState == AppState.idle
|
||||||
? childCount
|
? enableEmptyState
|
||||||
|
? 1
|
||||||
|
: childCount
|
||||||
: state.appState == AppState.busy
|
: state.appState == AppState.busy
|
||||||
? 3
|
? 3
|
||||||
: 1,
|
: 1,
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
import 'package:didvan/models/enums.dart';
|
import 'package:didvan/models/enums.dart';
|
||||||
import 'package:didvan/providers/core_provider.dart';
|
import 'package:didvan/providers/core_provider.dart';
|
||||||
|
import 'package:didvan/widgets/state_handlers/empty_connection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||||
|
|
||||||
class StateHandler<T extends CoreProvier> extends StatelessWidget {
|
class StateHandler<T extends CoreProvier> extends StatelessWidget {
|
||||||
final T state;
|
final T state;
|
||||||
final Widget Function(BuildContext context, T state) builder;
|
final Widget Function(BuildContext context, T state) builder;
|
||||||
final VoidCallback? onRefresh;
|
final VoidCallback onRetry;
|
||||||
final bool enableEmptyState;
|
final bool enableEmptyState;
|
||||||
final Widget? placeholder;
|
final Widget? placeholder;
|
||||||
final Widget? emptyState;
|
final Widget? emptyState;
|
||||||
|
|
@ -14,11 +15,11 @@ class StateHandler<T extends CoreProvier> extends StatelessWidget {
|
||||||
const StateHandler({
|
const StateHandler({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.builder,
|
required this.builder,
|
||||||
|
required this.onRetry,
|
||||||
|
required this.state,
|
||||||
this.emptyState,
|
this.emptyState,
|
||||||
this.enableEmptyState = false,
|
this.enableEmptyState = false,
|
||||||
this.onRefresh,
|
|
||||||
this.topPadding = 0,
|
this.topPadding = 0,
|
||||||
required this.state,
|
|
||||||
this.placeholder,
|
this.placeholder,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
|
@ -42,7 +43,7 @@ class StateHandler<T extends CoreProvier> extends StatelessWidget {
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
);
|
);
|
||||||
case AppState.failed:
|
case AppState.failed:
|
||||||
return Container();
|
return EmptyConnection(onRetry: onRetry);
|
||||||
default:
|
default:
|
||||||
return Container();
|
return Container();
|
||||||
}
|
}
|
||||||
14
pubspec.lock
|
|
@ -160,6 +160,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.0"
|
version: "3.3.0"
|
||||||
|
flutter_html:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_html
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0-alpha.2"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
|
@ -378,6 +385,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
|
numerus:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: numerus
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
octo_image:
|
octo_image:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
13
pubspec.yaml
|
|
@ -58,6 +58,7 @@ dependencies:
|
||||||
image_cropper: ^1.4.1
|
image_cropper: ^1.4.1
|
||||||
bot_toast: ^4.0.1
|
bot_toast: ^4.0.1
|
||||||
flutter_secure_storage: ^5.0.2
|
flutter_secure_storage: ^5.0.2
|
||||||
|
flutter_html: ^3.0.0-alpha.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
@ -103,6 +104,18 @@ flutter:
|
||||||
- lib/assets/images/themes/theme-dark.svg
|
- lib/assets/images/themes/theme-dark.svg
|
||||||
- lib/assets/images/records/record-dark.svg
|
- lib/assets/images/records/record-dark.svg
|
||||||
- lib/assets/images/records/record-light.svg
|
- lib/assets/images/records/record-light.svg
|
||||||
|
- lib/assets/images/empty_states/bookmark-light.svg
|
||||||
|
- lib/assets/images/empty_states/chart-light.svg
|
||||||
|
- lib/assets/images/empty_states/chat-light.svg
|
||||||
|
- lib/assets/images/empty_states/connection-light.svg
|
||||||
|
- lib/assets/images/empty_states/result-light.svg
|
||||||
|
- lib/assets/images/empty_states/studio-light.svg
|
||||||
|
- lib/assets/images/empty_states/bookmark-dark.svg
|
||||||
|
- lib/assets/images/empty_states/chart-dark.svg
|
||||||
|
- lib/assets/images/empty_states/chat-dark.svg
|
||||||
|
- lib/assets/images/empty_states/connection-dark.svg
|
||||||
|
- lib/assets/images/empty_states/result-dark.svg
|
||||||
|
- lib/assets/images/empty_states/studio-dark.svg
|
||||||
- lib/assets/animations/indicator-light.riv
|
- lib/assets/animations/indicator-light.riv
|
||||||
- lib/assets/animations/indicator-dark.riv
|
- lib/assets/animations/indicator-dark.riv
|
||||||
|
|
||||||
|
|
|
||||||