Add Infography Screen

This commit is contained in:
mehrdad78 2024-03-22 12:31:13 +03:30
parent 445b37bc91
commit ba20b25ab0
19 changed files with 756 additions and 115 deletions

View File

@ -1,3 +1,3 @@
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx4608m
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true

View File

@ -29,20 +29,20 @@ class LightThemeConfig {
); );
static const ColorScheme _colorScheme = ColorScheme( static const ColorScheme _colorScheme = ColorScheme(
primary: _primary, primary: _primary,
primaryContainer: _white, primaryContainer: _white,
secondary: Color(0xFFB20436), secondary: Color(0xFFB20436),
secondaryContainer: _white, secondaryContainer: _white,
surface: _white, surface: _white,
background: _background, background: _background,
error: Color(0xFFF00505), error: Color(0xFFF00505),
onPrimary: _white, onPrimary: _white,
onSecondary: _white, onSecondary: _white,
onSurface: _black, onSurface: _black,
onBackground: _white, onBackground: _white,
onError: _white, onError: _white,
brightness: Brightness.light, brightness: Brightness.light,
); outline: Color(0xff007EA7));
} }
class DarkThemeConfig { class DarkThemeConfig {

View File

@ -0,0 +1,29 @@
class InfoTagModel {
final List<InTag> tags;
InfoTagModel({required this.tags});
factory InfoTagModel.fromJson(Map<String, dynamic> json) {
return InfoTagModel(
tags: List<InTag>.from(json['tags'].map((x) => InTag.fromJson(x))),
);
}
}
class InTag {
final int id;
final String label;
InTag({required this.id, required this.label});
factory InTag.fromJson(Map<String, dynamic> json) {
return InTag(
id: json['id'],
label: json['label'],
);
}
@override
String toString() {
return label;
}
}

View File

@ -0,0 +1,53 @@
class InfographyContent {
final List<Content> contents;
final int lastPage;
InfographyContent({required this.contents, required this.lastPage});
factory InfographyContent.fromJson(Map<String, dynamic> json) {
return InfographyContent(
contents:
List<Content>.from(json['contents'].map((x) => Content.fromJson(x))),
lastPage: json['lastPage'],
);
}
}
class Content {
final int id;
final String title;
final String image;
final String category;
final List<Tag> tags;
Content(
{required this.id,
required this.title,
required this.image,
required this.category,
required this.tags});
factory Content.fromJson(Map<String, dynamic> json) {
return Content(
id: json['id'],
title: json['title'],
image: json['image'],
category: json['category'],
tags: List<Tag>.from(json['tags'].map((x) => Tag.fromJson(x))),
);
}
}
class Tag {
final int id;
final String label;
Tag({required this.id, required this.label});
factory Tag.fromJson(Map<String, dynamic> json) {
return Tag(
id: json['id'],
label: json['label'],
);
}
}

View File

@ -0,0 +1,13 @@
class InfographyRequestArgs {
final int page;
final String? q;
final List<int>? tag;
final List<int>? categories;
const InfographyRequestArgs({
required this.page,
this.categories,
this.q,
this.tag,
});
}

View File

@ -7,6 +7,8 @@ import 'package:didvan/views/direct/direct_state.dart';
import 'package:didvan/views/hashtag/hashtag.dart'; import 'package:didvan/views/hashtag/hashtag.dart';
import 'package:didvan/views/hashtag/hashtag_state.dart'; import 'package:didvan/views/hashtag/hashtag_state.dart';
import 'package:didvan/views/home/home.dart'; import 'package:didvan/views/home/home.dart';
import 'package:didvan/views/home/infography/infography_screen.dart';
import 'package:didvan/views/home/infography/infography_screen_state.dart';
import 'package:didvan/views/home/main/main_page_state.dart'; import 'package:didvan/views/home/main/main_page_state.dart';
import 'package:didvan/views/home/home_state.dart'; import 'package:didvan/views/home/home_state.dart';
import 'package:didvan/views/news/news.dart'; import 'package:didvan/views/news/news.dart';
@ -111,6 +113,12 @@ class RouteGenerator {
child: const News(), child: const News(),
), ),
); );
case Routes.infography:
return _createRoute(ChangeNotifierProvider<InfographyScreenState>(
create: (context) => InfographyScreenState(),
child: const InfographyScreen(),
));
case Routes.radarDetails: case Routes.radarDetails:
return _createRoute( return _createRoute(
ChangeNotifierProvider<RadarDetailsState>( ChangeNotifierProvider<RadarDetailsState>(

View File

@ -3,6 +3,7 @@ class Routes {
static const String home = '/home'; static const String home = '/home';
static const String radars = '/radars'; static const String radars = '/radars';
static const String news = '/news'; static const String news = '/news';
static const String infography = '/infography';
static const String podcasts = '/podcasts'; static const String podcasts = '/podcasts';
static const String videocasts = '/videocasts'; static const String videocasts = '/videocasts';
static const String aboutUs = '/about-us'; static const String aboutUs = '/about-us';

View File

@ -178,6 +178,7 @@ class RequestService {
void _handleResponse(http.Response? response) { void _handleResponse(http.Response? response) {
statusCode = response?.statusCode; statusCode = response?.statusCode;
//log("Mehradad->" + response!.body.toString());
if (_handleError(response)) { if (_handleError(response)) {
if (response!.body.isNotEmpty) { if (response!.body.isNotEmpty) {
_body = json.decode(response.body); _body = json.decode(response.body);

View File

@ -1,3 +1,4 @@
import 'package:didvan/models/requests/infography.dart';
import 'package:didvan/models/requests/news.dart'; import 'package:didvan/models/requests/news.dart';
import 'package:didvan/models/requests/radar.dart'; import 'package:didvan/models/requests/radar.dart';
import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/models/requests/studio.dart';
@ -7,6 +8,8 @@ class RequestHelper {
static const String _baseUserUrl = '$baseUrl/user'; static const String _baseUserUrl = '$baseUrl/user';
static const String _baseRadarUrl = '$baseUrl/radar'; static const String _baseRadarUrl = '$baseUrl/radar';
static const String _baseNewsUrl = '$baseUrl/news'; static const String _baseNewsUrl = '$baseUrl/news';
static const String _baseInfographyUrl = '$baseUrl/home/infography';
static const String _baseInfoTagUrl = '$baseUrl/home/infography/tags';
static const String _baseStudioUrl = '$baseUrl/studio'; static const String _baseStudioUrl = '$baseUrl/studio';
static const String _baseStatisticUrl = '$baseUrl/statistic'; static const String _baseStatisticUrl = '$baseUrl/statistic';
static const String _baseDirectUrl = '$_baseUserUrl/direct'; static const String _baseDirectUrl = '$_baseUserUrl/direct';
@ -106,6 +109,17 @@ class RequestHelper {
MapEntry('search', args.search), MapEntry('search', args.search),
]); ]);
static String infographyOverviews({required InfographyRequestArgs args}) =>
_baseInfographyUrl +
_urlConcatGenerator([
MapEntry('page', args.page),
MapEntry('category', _urlListConcatGenerator(args.categories)),
MapEntry('q', args.q),
MapEntry('tag', _urlListConcatGenerator(args.tag)),
]);
static String infographyTags() => _baseInfoTagUrl;
static String studioSlider(String type) => static String studioSlider(String type) =>
'$_baseStudioUrl/slider${_urlConcatGenerator([MapEntry('type', type)])}'; '$_baseStudioUrl/slider${_urlConcatGenerator([MapEntry('type', type)])}';
static String studioDetails(int id, StudioRequestArgs args) => static String studioDetails(int id, StudioRequestArgs args) =>

View File

@ -0,0 +1,184 @@
import 'dart:async';
import 'dart:developer';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/category.dart';
import 'package:didvan/models/infography/info_tag.dart';
import 'package:didvan/models/view/action_sheet_data.dart';
import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/views/home/infography/infography_screen_state.dart';
import 'package:didvan/views/home/main/widgets/infography_item.dart';
import 'package:didvan/views/widgets/didvan/checkbox.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/item_title.dart';
import 'package:didvan/views/widgets/search_field.dart';
import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:animated_custom_dropdown/custom_dropdown.dart';
class InfographyScreen extends StatefulWidget {
const InfographyScreen({super.key});
@override
State<InfographyScreen> createState() => _InfographyScreenState();
}
class _InfographyScreenState extends State<InfographyScreen> {
final ScrollController _scrollController = ScrollController();
int pageNumber = 1;
Timer? _timer;
final _focusNode = FocusNode();
@override
void initState() {
context.read<InfographyScreenState>().init();
_scrollController.addListener(_onScroll);
super.initState();
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
void _onScroll() {
if (_scrollController.position.pixels >=
_scrollController.position.maxScrollExtent) {
pageNumber++;
context
.read<InfographyScreenState>()
.getInfographyContent(page: pageNumber);
}
}
void _onChanged(String value) {
final state = context.read<InfographyScreenState>();
if (value.length < 3 && value.isNotEmpty || state.lastSearch == value) {
return;
}
_timer?.cancel();
_timer = Timer(const Duration(seconds: 1), () {
state.search = value;
state.getInfographyContent(page: 1);
});
}
Future<void> _showFilterBottomSheet() async {
final state = context.read<InfographyScreenState>();
await ActionSheetUtils.showBottomSheet(
data: ActionSheetData(
title: 'فیلتر جستجو',
smallDismissButton: true,
titleIcon: DidvanIcons.filter_regular,
dismissTitle: 'حذف فیلتر',
confrimTitle: 'نمایش نتایج',
onDismissed: () => state.resetFilters(false),
onConfirmed: () => state.getInfographyContent(page: 1),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ItemTitle(
title: "جستجوی هشتگ",
icon: DidvanIcons.hashtag_regular,
style: Theme.of(context).textTheme.bodyMedium,
),
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
child: CustomDropdown<InTag>.multiSelect(
closedHeaderPadding: EdgeInsets.all(12),
items: state.tags,
decoration: CustomDropdownDecoration(
closedBorder: Border.all(color: Colors.grey),
closedFillColor: Colors.grey.shade100.withOpacity(0.1),
),
hintText: "انتخاب کنید",
onListChanged: (value) {
state.selectedTags.addAll(value);
log('changing value to: ${value.map((e) => e.label + e.id.toString())}');
},
),
),
ItemTitle(
title: 'دسته بندی',
icon: DidvanIcons.category_regular,
style: Theme.of(context).textTheme.bodyMedium,
),
const SizedBox(height: 12),
Wrap(
children: [
for (var i = 0; i < state.categories.length; i++)
SizedBox(
width: (MediaQuery.of(context).size.width - 40) / 2,
child: DidvanCheckbox(
title: state.categories[i].label,
value: state.selectedCats.contains(state.categories[i]),
onChanged: (value) {
if (value) {
state.selectedCats.add(state.categories[i]);
return;
}
state.selectedCats.remove(state.categories[i]);
},
),
),
],
),
],
),
),
);
}
@override
Widget build(BuildContext context) {
return StateHandler<InfographyScreenState>(
onRetry: context.read<InfographyScreenState>().init,
state: context.watch<InfographyScreenState>(),
builder: (context, state) {
return Scaffold(
appBar: AppBar(
elevation: 0.0,
scrolledUnderElevation: 0.0,
title: DidvanText(
"اینفوگرافی",
style: Theme.of(context).textTheme.bodyLarge,
fontSize: 20,
),
),
body: Column(
children: [
Container(
height: 80,
color: Colors.white,
padding: const EdgeInsets.symmetric(
horizontal: 16.0, vertical: 16.0),
child: SearchField(
title: "اینفوگرافی",
onChanged: _onChanged,
focusNode: _focusNode,
onFilterButtonPressed: _showFilterBottomSheet,
isFiltered: state.filtering),
),
Expanded(
child: ListView.builder(
controller: _scrollController,
//physics: const ClampingScrollPhysics(),
itemCount: state.contents.length,
itemBuilder: (context, index) => InfographyItem(
image: state.contents[index].image,
category: state.contents[index].category,
title: state.contents[index].title,
tag: state.contents[index].tags,
),
),
),
],
),
);
},
);
}
}

View File

@ -0,0 +1,127 @@
import 'package:didvan/constants/assets.dart';
import 'package:didvan/models/category.dart';
import 'package:didvan/models/enums.dart';
import 'package:didvan/models/infography/info_tag.dart';
import 'package:didvan/models/infography/infography_content.dart';
import 'package:didvan/models/requests/infography.dart';
import 'package:didvan/providers/core.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart';
import 'dart:developer';
import 'package:didvan/views/widgets/infography_tag.dart';
class InfographyScreenState extends CoreProvier {
List<Content> contents = [];
String search = '';
String lastSearch = '';
int lastPage = 1;
int page = 1;
final List<InTag> selectedTags = [];
List<InTag> tags = [];
final List<CategoryData> selectedCats = [];
List<CategoryData> categories = [];
bool isScrolled = false;
bool shouldColapse = false;
bool get filtering => selectedCats.length > 1 && selectedTags.length > 1;
bool get searching => search.isNotEmpty;
bool get isColapsed =>
(selectedCats.length == 1 &&
selectedTags.length == 1 &&
!filtering &&
isScrolled) ||
isScrolled;
bool get isCategorySelected =>
selectedCats.length == 1 && selectedTags.length == 1 && !filtering;
Future<void> getInfographyContent({required int page}) async {
this.page = page;
lastSearch = search;
if (page == 1) {
contents.clear();
tags.clear();
appState = AppState.busy;
}
final service = RequestService(RequestHelper.infographyOverviews(
args: InfographyRequestArgs(
page: page,
tag: selectedTags.map((e) => e.id).toList(),
q: search == '' ? null : search,
categories: selectedCats.map((e) => e.id).toList())));
final service2 = RequestService(RequestHelper.infographyTags());
await service.httpGet();
await service2.httpGet();
if (service.isSuccess && service2.isSuccess) {
lastPage = service.result['lastPage'];
final content = InfographyContent.fromJson(service.result);
final content2 = InfoTagModel.fromJson(service2.result);
contents.addAll(content.contents);
tags.addAll(content2.tags);
appState = AppState.idle;
return;
}
appState = AppState.failed;
}
void resetFilters(bool isInit) {
selectedCats.clear();
selectedTags.clear();
search = '';
lastSearch = '';
isScrolled = false;
if (!isInit) {
getInfographyContent(page: 1);
}
}
void init() {
// search = '';
// lastSearch = '';
resetFilters(true);
Future.delayed(Duration.zero, () {
getInfographyContent(page: 1);
});
categories = [
CategoryData(
id: 1,
label: 'اقتصادی',
asset: Assets.economicCategoryIcon,
),
CategoryData(
id: 2,
label: 'سیاسی',
asset: Assets.politicalCategoryIcon,
),
CategoryData(
id: 3,
label: 'فناوری',
asset: Assets.techCategoryIcon,
),
CategoryData(
id: 4,
label: 'کسب و کار',
asset: Assets.businessCategoryIcon,
),
CategoryData(
id: 5,
label: 'زیست محیطی',
asset: Assets.enviromentalCategoryIcon,
),
CategoryData(
id: 6,
label: 'اجتماعی',
asset: Assets.socialCategoryIcon,
),
];
}
}

View File

@ -1,6 +1,8 @@
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/home_page_content/home_page_list.dart'; import 'package:didvan/models/home_page_content/home_page_list.dart';
import 'package:didvan/routes/routes.dart';
import 'package:didvan/views/home/infography/infography_screen.dart';
import 'package:didvan/views/home/main/main_page_state.dart'; import 'package:didvan/views/home/main/main_page_state.dart';
import 'package:didvan/views/home/main/widgets/banner.dart'; import 'package:didvan/views/home/main/widgets/banner.dart';
import 'package:didvan/views/home/main/widgets/general_item.dart'; import 'package:didvan/views/home/main/widgets/general_item.dart';
@ -41,10 +43,54 @@ class _MainPageState extends State<MainPage> {
} }
index--; index--;
if (index == 4) { if (index == 4) {
return const Padding( return Padding(
padding: EdgeInsets.only(top: 32), padding: const EdgeInsets.only(top: 32),
child: MainPageBanner( child: Column(
isFirst: false, children: [
Padding(
padding: const EdgeInsets.only(
left: 16,
right: 16,
bottom: 16,
top: 28,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
const SizedBox(width: 4),
DidvanText(
"اینفوگرافی",
style: Theme.of(context).textTheme.titleMedium,
color: Theme.of(context).colorScheme.title,
),
],
),
GestureDetector(
onTap: () => {
Navigator.of(context).pushNamed(Routes.infography)
},
child: Row(
children: [
DidvanText(
"همه",
color: Theme.of(context).colorScheme.primary,
),
Icon(
DidvanIcons.angle_left_light,
color: Theme.of(context).colorScheme.primary,
)
],
),
)
],
),
),
const MainPageBanner(
isFirst: false,
),
],
), ),
); );
} }
@ -162,30 +208,6 @@ class _MainPageSection extends StatelessWidget {
), ),
), ),
), ),
// if (list.type == 'radar')
// DidvanSlider(
// height: 260,
// itemCount: list.contents.length,
// viewportFraction: 0.65,
// itemBuilder: (context, index, realIndex) => Padding(
// padding: const EdgeInsets.symmetric(horizontal: 4),
// child: MainPageGeneralItem(
// content: list.contents[index],
// ),
// ),
// ),
// if (list.type == 'video')
// DidvanSlider(
// height: 260,
// itemCount: list.contents.length,
// viewportFraction: 0.65,
// itemBuilder: (context, index, realIndex) => Padding(
// padding: const EdgeInsets.symmetric(horizontal: 4),
// child: MainPageGeneralItem(
// content: list.contents[index],
// ),
// ),
// ),
if (list.type == 'podcast') if (list.type == 'podcast')
Padding( Padding(
padding: const EdgeInsets.only(top: 28), padding: const EdgeInsets.only(top: 28),
@ -206,44 +228,7 @@ class _MainPageSection extends StatelessWidget {
.toList(), .toList(),
), ),
), ),
// if (list.type != 'news' &&
// list.type != 'radar' &&
// list.type != 'video' &&
// list.type != 'podcast')
// DidvanSlider(
// itemBuilder: (context, index, realIndex) => Padding(
// padding: const EdgeInsets.symmetric(horizontal: 4),
// child: MainPageGeneralItem(
// content: list.contents[index],
// ),
// ),
// itemCount: list.contents.length,
// viewportFraction: 0.65,
// height: 260 + _maxSublistCount() * 20,
// ),
// if (!isLast) const _MainPageDivider(),
], ],
); );
} }
} }
// class _MainPageDivider extends StatelessWidget {
// const _MainPageDivider();
// @override
// Widget build(BuildContext context) {
// return Container(
// height: 2,
// margin: const EdgeInsets.only(
// top: 8,
// left: 20,
// right: 20,
// ),
// width: double.infinity,
// decoration: BoxDecoration(
// borderRadius: DesignConfig.highBorderRadius,
// color: Theme.of(context).colorScheme.border,
// ),
// );
// }
// }

View File

@ -1,5 +1,6 @@
import 'package:didvan/models/enums.dart'; import 'package:didvan/models/enums.dart';
import 'package:didvan/models/home_page_content/home_page_content.dart'; import 'package:didvan/models/home_page_content/home_page_content.dart';
import 'package:didvan/models/requests/infography.dart';
import 'package:didvan/models/requests/news.dart'; import 'package:didvan/models/requests/news.dart';
import 'package:didvan/models/requests/radar.dart'; import 'package:didvan/models/requests/radar.dart';
import 'package:didvan/providers/core.dart'; import 'package:didvan/providers/core.dart';
@ -49,6 +50,17 @@ class MainPageState extends CoreProvier {
link = link ?? ''; link = link ?? '';
dynamic args; dynamic args;
switch (type) { switch (type) {
case 'infography':
{
link = Routes.infography;
args = {
'onMarkChanged': (id, value) => markChangeHandler(type, id, value),
'id': id,
'args': const InfographyRequestArgs(page: 0),
'hasUnmarkConfirmation': false,
};
break;
}
case 'news': case 'news':
{ {
link = Routes.newsDetails; link = Routes.newsDetails;

View File

@ -0,0 +1,136 @@
import 'dart:math';
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/infography/infography_content.dart';
import 'package:didvan/views/widgets/animated_visibility.dart';
import 'package:didvan/views/widgets/didvan/card.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/infography_tag.dart';
import 'package:didvan/views/widgets/ink_wrapper.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/material.dart';
class _BackButton extends StatefulWidget {
const _BackButton({Key? key}) : super(key: key);
@override
__BackButtonState createState() => __BackButtonState();
}
class __BackButtonState extends State<_BackButton> {
@override
Widget build(BuildContext context) {
return AnimatedVisibility(
duration: DesignConfig.lowAnimationDuration,
isVisible: true,
child: InkWrapper(
borderRadius: DesignConfig.lowBorderRadius,
onPressed: Navigator.of(context).pop,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.splash,
border: Border.all(color: Theme.of(context).colorScheme.border),
borderRadius: DesignConfig.lowBorderRadius,
),
child: const Icon(
DidvanIcons.back_regular,
size: 32,
),
),
),
);
}
}
class InfographyItem extends StatelessWidget {
final String image;
final String title;
final String category;
final List<Tag> tag;
const InfographyItem(
{super.key,
required this.image,
required this.category,
required this.title,
required this.tag});
void _openInteractiveViewer(BuildContext context, String image) {
showDialog(
context: context,
builder: (context) => Stack(
children: [
Positioned.fill(
child: InteractiveViewer(
child: Center(
child: SkeletonImage(
width: min(MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height),
imageUrl: image,
),
),
),
),
const Positioned(
right: 24,
top: 24,
child: _BackButton(),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return DidvanCard(
margin: const EdgeInsets.all(12),
padding: const EdgeInsets.all(12),
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Align(
alignment: Alignment.centerRight,
child: DidvanText(
title,
style: Theme.of(context).textTheme.bodyLarge,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: GestureDetector(
onTap: () => _openInteractiveViewer(context, image),
child: SkeletonImage(
imageUrl: image,
aspectRatio: 16 / 9,
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Wrap(
spacing: 4,
runSpacing: 4,
children: [
for (var i = 0; i < tag.length; i++)
InfographyTag(
tag: tag[i],
),
],
),
InfoCat(category: category)
],
)
],
),
);
}
}

View File

@ -15,16 +15,6 @@ class MainPageMainContent extends StatelessWidget {
isFirst: true, isFirst: true,
), ),
const SizedBox(height: 28), const SizedBox(height: 28),
// Stack(
// children: [
// Positioned(
// bottom: 13,
// child: Container(
// width: MediaQuery.of(context).size.width,
// height: 2,
// color: Theme.of(context).colorScheme.border,
// ),
// ),
Center( Center(
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12), padding: const EdgeInsets.symmetric(horizontal: 12),

View File

@ -0,0 +1,85 @@
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/infography/infography_content.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/ink_wrapper.dart';
import 'package:flutter/material.dart';
class InfographyTag extends StatelessWidget {
final Tag tag;
const InfographyTag({
Key? key,
required this.tag,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return InkWrapper(
borderRadius: DesignConfig.lowBorderRadius,
onPressed: () => {},
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 4,
),
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,
size: 16,
),
DidvanText(
tag.label,
color: Theme.of(context).colorScheme.focusedBorder,
style: Theme.of(context).textTheme.labelLarge,
),
],
),
),
);
}
}
class InfoCat extends StatelessWidget {
final String category;
const InfoCat({
Key? key,
required this.category,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return InkWrapper(
borderRadius: DesignConfig.lowBorderRadius,
onPressed: () => {},
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 8,
),
decoration: BoxDecoration(
borderRadius: DesignConfig.lowBorderRadius,
border: Border.all(
color: Theme.of(context).colorScheme.outline,
),
),
child: DidvanText(
category,
color: Theme.of(context).colorScheme.outline,
style: Theme.of(context).textTheme.labelLarge,
),
),
);
}
}

View File

@ -31,7 +31,9 @@ class _SearchFieldState extends State<SearchField> {
@override @override
void initState() { void initState() {
widget.focusNode.addListener(() { widget.focusNode.addListener(() {
setState(() {}); if (mounted) {
setState(() {});
}
}); });
super.initState(); super.initState();
} }

View File

@ -9,6 +9,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.7" version: "1.3.7"
animated_custom_dropdown:
dependency: "direct main"
description:
name: animated_custom_dropdown
sha256: "9e286defa42f2e774285015d7ff29523a24260888d53f79a3635328fad5bdad7"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -134,10 +142,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: collection name: collection
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.17.2" version: "1.18.0"
cross_file: cross_file:
dependency: transitive dependency: transitive
description: description:
@ -442,10 +450,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: fwfh_text_style name: fwfh_text_style
sha256: f0883ccb64b7bb3f2a7a091542c2e834fc3e2a6aa54158f46b3c43b55675d8f7 sha256: "5f8b587fd223a6bf14aad3d3da5e7ced0628becbd0768f8e7ae25ff6b9f3d2ec"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.22.8+3" version: "2.23.8"
graphs: graphs:
dependency: transitive dependency: transitive
description: description:
@ -598,14 +606,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.2" version: "1.0.2"
logging:
dependency: transitive
description:
name: logging
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
@ -626,10 +626,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.1" version: "1.10.0"
mime: mime:
dependency: transitive dependency: transitive
description: description:
@ -927,18 +927,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: stack_trace name: stack_trace
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.11.1"
stream_channel: stream_channel:
dependency: transitive dependency: transitive
description: description:
name: stream_channel name: stream_channel
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" version: "2.1.2"
string_scanner: string_scanner:
dependency: transitive dependency: transitive
description: description:
@ -967,10 +967,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.0" version: "0.6.1"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@ -1151,10 +1151,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: web name: web
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.4-beta" version: "0.3.0"
webview_flutter: webview_flutter:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1212,5 +1212,5 @@ packages:
source: hosted source: hosted
version: "6.3.0" version: "6.3.0"
sdks: sdks:
dart: ">=3.1.0 <4.0.0" dart: ">=3.2.0-194.0.dev <4.0.0"
flutter: ">=3.13.0" flutter: ">=3.14.0-0"

View File

@ -69,6 +69,7 @@ dependencies:
assets_audio_player: ^3.1.1 assets_audio_player: ^3.1.1
fl_chart: ^0.63.0 fl_chart: ^0.63.0
collection: ^1.17.2 collection: ^1.17.2
animated_custom_dropdown: ^3.0.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: