D1APP-47 empty states configuration

This commit is contained in:
MohammadTaha Basiri 2022-01-24 02:23:32 +03:30
parent e642d58e46
commit 17287f4b5f
27 changed files with 1063 additions and 83 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 59 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 59 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 43 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 44 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 39 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 44 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 51 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 52 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 42 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 42 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 46 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -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';

View File

@ -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,
@ -52,16 +54,20 @@ class _NewsState extends State<News> {
), ),
), ),
SliverStateHandler<NewsState>( SliverStateHandler<NewsState>(
onRetry: () => state.getNews(page: state.page),
state: state, state: state,
builder: (context, state, index) => NewsItem( builder: (context, state, index) => NewsItem(
news: state.news[index], news: state.news[index],
), ),
enableEmptyState: state.news.isEmpty,
emptyState: EmptyResult(
onNewSearch: () => _focusNode.requestFocus(),
),
childCount: state.news.length, childCount: state.news.length,
itemPadding: const EdgeInsets.only(left: 16, right: 16, bottom: 16), itemPadding: const EdgeInsets.only(left: 16, right: 16, bottom: 16),
placeholder: const _NewsItemPlaceholder(), placeholder: const _NewsItemPlaceholder(),
), ),
], ],
),
); );
} }

View File

@ -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,8 +59,7 @@ 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(
@ -70,6 +70,7 @@ class _RadarState extends State<Radar> {
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 &&
!state.searching &&
state.appState != AppState.failed)
const SliverToBoxAdapter( const SliverToBoxAdapter(
child: SizedBox(height: 276), 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(
@ -109,12 +114,16 @@ class _RadarState extends State<Radar> {
), ),
), ),
SliverStateHandler<RadarState>( SliverStateHandler<RadarState>(
onRetry: () => state.getRadarOverviews(page: state.page),
state: state, state: state,
itemPadding: const EdgeInsets.only( itemPadding: const EdgeInsets.only(
bottom: 20, bottom: 20,
left: 16, left: 16,
right: 16, right: 16,
), ),
enableEmptyState: state.radars.isEmpty,
emptyState:
EmptyResult(onNewSearch: () => _focusNode.requestFocus()),
placeholder: const _RadarItemPlaceholder(), placeholder: const _RadarItemPlaceholder(),
builder: (context, state, index) => RadarItem( builder: (context, state, index) => RadarItem(
radar: state.radars[index], radar: state.radars[index],
@ -127,20 +136,22 @@ class _RadarState extends State<Radar> {
), ),
], ],
), ),
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,
), ),
], ],
), ),
),
); );
} }

View File

@ -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,17 +46,22 @@ class RadarState extends CoreProvier {
Future<void> getRadarOverviews({ Future<void> getRadarOverviews({
required int page, required int page,
}) async { }) async {
if (this.page == page) {
radars.clear(); 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(
args: RadarRequestArgs(
page: page, page: page,
startDate: startDate?.split(' ').first, startDate: startDate?.split(' ').first,
endDate: endDate?.split(' ').first, endDate: endDate?.split(' ').first,
search: search == '' ? null : search, search: search == '' ? null : search,
categories: selectedCats.map((e) => e.id).toList(), categories: selectedCats.map((e) => e.id).toList(),
), ),
),
); );
await service.httpGet(); await service.httpGet();
if (service.isSuccess) { if (service.isSuccess) {

View File

@ -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: 'تنظیمات'),

View File

@ -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,
),
),
],
);
} }
} }

View File

@ -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,
),
),
],
);
} }
} }

View File

@ -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();
} }
} }

View File

@ -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: 'تلاش دوباره',
);
}
}

View File

@ -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,
);
}
}

View File

@ -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,
),
],
);
}
}

View File

@ -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,

View File

@ -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();
} }

View File

@ -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:

View File

@ -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