categories handle
This commit is contained in:
parent
5b993e5c4c
commit
5144f1c446
|
|
@ -10,6 +10,7 @@
|
|||
analyzer:
|
||||
errors:
|
||||
avoid_print: ignore
|
||||
deprecated_member_use: ignore
|
||||
deprecated_member_use_from_same_package: ignore
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
// This file is no longer needed and can be deleted.
|
||||
|
|
@ -1,19 +1,22 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:proxibuy/presentation/providers/cubit/user_info_cubit.dart';
|
||||
import 'package:proxibuy/presentation/providers/category/cubit/category_cubit.dart';
|
||||
import 'package:proxibuy/presentation/providers/user_info_cubit.dart';
|
||||
import 'package:proxibuy/presentation/ui/screens/auth/auth_page.dart';
|
||||
import 'package:proxibuy/presentation/ui/screens/categories/categories_page.dart';
|
||||
import 'package:proxibuy/presentation/ui/screens/home/explore_screen.dart';
|
||||
import 'package:proxibuy/presentation/ui/screens/home/home_page.dart';
|
||||
import 'package:proxibuy/presentation/ui/screens/home/home_screen.dart';
|
||||
import 'package:proxibuy/presentation/ui/screens/home/setting_screen.dart';
|
||||
import 'package:proxibuy/presentation/ui/screens/product/product_page.dart';
|
||||
import 'package:proxibuy/presentation/ui/widgets/navigations/drop_down_demo2.dart';
|
||||
|
||||
class AppRouter {
|
||||
static final initial = '/';
|
||||
static final explore = '/explore';
|
||||
static final setting = '/settings';
|
||||
static final product = '/product';
|
||||
static final categories = '/categories';
|
||||
|
||||
static List<String> home = [initial, explore, setting];
|
||||
|
||||
|
|
@ -79,9 +82,20 @@ class AppRouter {
|
|||
GoRoute(
|
||||
path: product,
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
return ProductPage();
|
||||
return DropDownDemo3();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '$categories/:id',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
final id = state.pathParameters['id']!;
|
||||
return BlocProvider<CategoryCubit>(
|
||||
create: (context) => CategoryCubit()..getCategory(id),
|
||||
child: CategoriesPage(
|
||||
id: id,
|
||||
),
|
||||
);
|
||||
},
|
||||
routes: <RouteBase>[],
|
||||
),
|
||||
]),
|
||||
]),
|
||||
|
|
|
|||
|
|
@ -10,8 +10,7 @@ class ApiService {
|
|||
late Dio _dio;
|
||||
|
||||
void setAuthToken(String token) {
|
||||
_dio.options.headers['Authorization'] =
|
||||
'Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJheC1zX3N3Tm5fU1hDTVkzWFowSDVKekNhQ0psVXh6bmZ0WHBxSk1YUEF3In0.eyJleHAiOjE3Mzk4ODcwNzksImlhdCI6MTczOTg4NTI3OSwianRpIjoiYTlmZGE4NzItZmJhZC00ZmQ5LTg3MTMtMTcwYjM3MWE1NTM2IiwiaXNzIjoiaHR0cHM6Ly9rZXljbG9hay5saWFyYS5ydW4vcmVhbG1zL2xiYSIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJmMzljODIxNi0zODhhLTQ0ZTEtODVhOC00Zjk5NmU2NmU2MDQiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJmcm9udGVuZCIsInNpZCI6IjIwNWNmNTBkLTE5MWUtNGViMS1iODBkLTMzMTFiNjIzYTZhMiIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cHM6Ly9sYmEtYXBpLmxpYXJhLnJ1bi8qIiwiaHR0cDovL2xvY2FsaG9zdDozMDAwLyoiLCIvKiJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1sYmEiLCJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19LCJmcm9udGVuZCI6eyJyb2xlcyI6WyJzaG9wIiwidXNlciJdfX0sInNjb3BlIjoib3BlbmlkIGVtYWlsIHByb2ZpbGUiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwibmFtZSI6ImRlbW8gZGVtbyIsInByZWZlcnJlZF91c2VybmFtZSI6ImRlbW8iLCJnaXZlbl9uYW1lIjoiZGVtbyIsImZhbWlseV9uYW1lIjoiZGVtbyIsImVtYWlsIjoiZGVtb0BnbWFpbC5jb20ifQ.r_RtuJLZ9HQZdoy1Fi29d7hbXqcQ497XThKUmAjd_ClenE2VjpTzpogTOFwrrzJqRxm9fDpBflliqBWRg7KwR7irxW4HHBBaJmCaLzLbG8EtiuvciwDc9ugqLwUvGs2Gnzc7P0RA9aWzVY2lBkDZa7GeEvCCI4k0pbSWsAjX-Ax2vHxCqW8GzMsBeLkQ2BE9cyKX5Q3f-yne5HjDoxF1350qPlXwxIRkmM2Ct-aiMm7CjCaawPdTqdritsWejwTtaVCQtzkHPyeToPwE_X1YLWDlFdwpWSzjiI2fDFyV-MykLgDZevxHtvFoIg-2f6Zsm2_t7AMoCr-tM7vquCZTaw';
|
||||
_dio.options.headers['Authorization'] = 'Bearer $token';
|
||||
}
|
||||
|
||||
ApiService() {
|
||||
|
|
@ -28,7 +27,8 @@ class ApiService {
|
|||
)..interceptors.add(PrettyDioLogger(
|
||||
enabled: kDebugMode,
|
||||
));
|
||||
setAuthToken('');
|
||||
setAuthToken(
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJheC1zX3N3Tm5fU1hDTVkzWFowSDVKekNhQ0psVXh6bmZ0WHBxSk1YUEF3In0.eyJleHAiOjE3Mzk5NzkwNDUsImlhdCI6MTczOTk0MzA0NSwianRpIjoiMzY2NDllZWQtNTBiNy00ZWYyLWIyNmUtOTcxZDUwODBhODc1IiwiaXNzIjoiaHR0cHM6Ly9rZXljbG9hay5saWFyYS5ydW4vcmVhbG1zL2xiYSIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJmMzljODIxNi0zODhhLTQ0ZTEtODVhOC00Zjk5NmU2NmU2MDQiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJmcm9udGVuZCIsInNpZCI6IjcwZDlhNDhmLTcxMDktNGYxNi1hNTQ5LTVmNjE3MDk4ZTJmMiIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cHM6Ly9sYmEtYXBpLmxpYXJhLnJ1bi8qIiwiaHR0cDovL2xvY2FsaG9zdDozMDAwLyoiLCIvKiJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1sYmEiLCJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19LCJmcm9udGVuZCI6eyJyb2xlcyI6WyJzaG9wIiwidXNlciJdfX0sInNjb3BlIjoib3BlbmlkIGVtYWlsIHByb2ZpbGUiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwibmFtZSI6ImRlbW8gZGVtbyIsInByZWZlcnJlZF91c2VybmFtZSI6ImRlbW8iLCJnaXZlbl9uYW1lIjoiZGVtbyIsImZhbWlseV9uYW1lIjoiZGVtbyIsImVtYWlsIjoiZGVtb0BnbWFpbC5jb20ifQ.H3MssiP94FiWyc7FqfoSK7Zt58VOZmunM1D-wrseDiTQF2lFIfCMYbWkKv1ko8hn-zn-1ExV_6auFCNS0C_wTyFbGq_IHwYQr9nGqji_cr4dum19doASpfZRQiR_oR5RM96Ht5lV3nY_X7o-ksJEiRDOHUZ-xmDLkxhGWfeTO90DXYWv_S39mS55R7SsQz7PI83B7ya9qgp-5GND_oY3iNjDYVTI46EQuGOTiNyLgUrRk64IFy4Bbhp-EVj7QhGwkDEOosAytzE5aqW98-1GUSfUS77P36Ln0olQEb_uYed8EDkdauAIPN-iN8Eg4q7QmT-fpBCP61dcy04FRfTzPw');
|
||||
}
|
||||
|
||||
/// 🔹 Handle GET requests
|
||||
|
|
|
|||
|
|
@ -47,10 +47,9 @@ class Meta {
|
|||
currentPage = json['currentPage'];
|
||||
totalPages = json['totalPages'];
|
||||
if (json['sortBy'] != null) {
|
||||
// sortBy = <List<String>>[];
|
||||
// json['sortBy'].forEach((v) {
|
||||
// sortBy!.addAll(v.toList());
|
||||
// });
|
||||
sortBy = (json['sortBy'] as List)
|
||||
.map((item) => (item as List).map((e) => e.toString()).toList())
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,24 +1,56 @@
|
|||
class CategoriesModel {
|
||||
String? id;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
String? state;
|
||||
String? name;
|
||||
int? count;
|
||||
String? description;
|
||||
String? createdBy;
|
||||
String? parentId;
|
||||
String? icon;
|
||||
String? url;
|
||||
|
||||
CategoriesModel({this.id, this.name, this.count, this.parentId});
|
||||
CategoriesModel(
|
||||
{this.id,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.state,
|
||||
this.name,
|
||||
this.count,
|
||||
this.description,
|
||||
this.createdBy,
|
||||
this.parentId,
|
||||
this.icon,
|
||||
this.url});
|
||||
|
||||
CategoriesModel.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
createdAt = json['createdAt'];
|
||||
updatedAt = json['updatedAt'];
|
||||
state = json['state'];
|
||||
name = json['name'];
|
||||
count = json['count'];
|
||||
description = json['description'];
|
||||
createdBy = json['created_by'];
|
||||
parentId = json['parent_id'];
|
||||
icon = json['icon'];
|
||||
url = json['url'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['id'] = id;
|
||||
data['createdAt'] = createdAt;
|
||||
data['updatedAt'] = updatedAt;
|
||||
data['state'] = state;
|
||||
data['name'] = name;
|
||||
data['count'] = count;
|
||||
data['description'] = description;
|
||||
data['created_by'] = createdBy;
|
||||
data['parent_id'] = parentId;
|
||||
data['icon'] = icon;
|
||||
data['url'] = url;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ import 'package:proxibuy/core/services/api/response_model.dart';
|
|||
import 'package:proxibuy/data/models/categories/categories_model.dart';
|
||||
|
||||
class CategoryRepository {
|
||||
static Future<List<CategoriesModel>?> fetchAll() async {
|
||||
static Future<ResponseModel<List<CategoriesModel>>> fetchAll(
|
||||
{int page = 1}) async {
|
||||
try {
|
||||
var response = await apiService.get(ApiRoutes.category);
|
||||
print("Users: $response");
|
||||
var response = await apiService.get('${ApiRoutes.category}?page=$page');
|
||||
final res = ResponseModel<List<CategoriesModel>>.fromJson(
|
||||
response,
|
||||
(data) {
|
||||
|
|
@ -16,7 +16,37 @@ class CategoryRepository {
|
|||
.toList();
|
||||
},
|
||||
);
|
||||
return res.data;
|
||||
return res;
|
||||
} catch (e) {
|
||||
print("Error: $e");
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<ResponseModel<List<CategoriesModel>>> fetchAllChild(
|
||||
{int page = 1, required final String id}) async {
|
||||
try {
|
||||
var response = await apiService
|
||||
.get('${ApiRoutes.category}?page=$page?filter.parent_id=$id');
|
||||
final res = ResponseModel<List<CategoriesModel>>.fromJson(
|
||||
response,
|
||||
(data) {
|
||||
return (data as List)
|
||||
.map((item) => CategoriesModel.fromJson(item))
|
||||
.toList();
|
||||
},
|
||||
);
|
||||
return res;
|
||||
} catch (e) {
|
||||
print("Error: $e");
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<CategoriesModel> fetchOne({required final String id}) async {
|
||||
try {
|
||||
var response = await apiService.get('${ApiRoutes.category}/$id');
|
||||
return CategoriesModel.fromJson(response['category']);
|
||||
} catch (e) {
|
||||
print("Error: $e");
|
||||
rethrow;
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@ import 'package:flutter_native_splash/flutter_native_splash.dart';
|
|||
import 'package:proxibuy/core/routes/app_router.dart';
|
||||
import 'package:proxibuy/core/utils/my_custom_scroll_behavior.dart';
|
||||
import 'package:proxibuy/data/storage/shared_preferences_helper.dart';
|
||||
import 'package:proxibuy/presentation/providers/cubit/categories_cubit.dart';
|
||||
import 'package:proxibuy/presentation/providers/cubit/them_mode_cubit.dart';
|
||||
import 'package:proxibuy/presentation/providers/cubit/user_info_cubit.dart';
|
||||
import 'package:proxibuy/presentation/providers/category/cubit/categories_children_cubit.dart';
|
||||
import 'package:proxibuy/presentation/providers/category/cubit/categories_cubit.dart';
|
||||
import 'package:proxibuy/presentation/providers/them_mode_cubit.dart';
|
||||
import 'package:proxibuy/presentation/providers/user_info_cubit.dart';
|
||||
import 'package:proxibuy/presentation/ui/theme/theme.dart';
|
||||
import 'package:url_strategy/url_strategy.dart';
|
||||
|
||||
|
|
@ -19,6 +20,8 @@ void main() async {
|
|||
runApp(MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider<ThemModeCubit>(create: (context) => ThemModeCubit()),
|
||||
BlocProvider<CategoriesChildrenCubit>(
|
||||
create: (context) => CategoriesChildrenCubit()),
|
||||
BlocProvider<CategoriesCubit>(create: (context) => CategoriesCubit()),
|
||||
BlocProvider<UserInfoCubit>(
|
||||
create: (context) => UserInfoCubit()..getUserInfo()),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:proxibuy/data/models/categories/categories_model.dart';
|
||||
import 'package:proxibuy/data/repositories/category_repository.dart';
|
||||
|
||||
part 'categories_children_state.dart';
|
||||
|
||||
class CategoriesChildrenCubit extends Cubit<CategoriesChildrenState> {
|
||||
CategoriesChildrenCubit() : super(CategoriesChildrenInitial());
|
||||
|
||||
void updateCategories(List<CategoriesModel> newCategories) {
|
||||
emit(state.copyWith(categories: newCategories));
|
||||
}
|
||||
|
||||
Future<void> getAllChildCategories(String id) async {
|
||||
if (state.isLoading || state.currentPage > state.totalPages) return;
|
||||
if (state.currentPage == 1) {
|
||||
emit(CategoriesChildrenLoading());
|
||||
} else {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
}
|
||||
try {
|
||||
final response = await CategoryRepository.fetchAllChild(
|
||||
page: state.currentPage, id: id);
|
||||
emit(CategoriesChildrenLoaded(
|
||||
categories: List.from(state.categories)..addAll(response.data ?? []),
|
||||
isLoading: false,
|
||||
currentPage: state.currentPage + 1,
|
||||
totalPages: response.meta?.totalPages ?? 1,
|
||||
));
|
||||
} catch (e) {
|
||||
emit(CategoriesChildrenError(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future resetPagination() async {
|
||||
emit(CategoriesChildrenInitial());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
part of 'categories_children_cubit.dart';
|
||||
|
||||
class CategoriesChildrenState {
|
||||
final List<CategoriesModel> categories;
|
||||
final bool isLoading;
|
||||
final String? errorMessage;
|
||||
final int currentPage;
|
||||
final int totalPages;
|
||||
|
||||
const CategoriesChildrenState({
|
||||
this.categories = const [],
|
||||
this.isLoading = false,
|
||||
this.errorMessage,
|
||||
this.currentPage = 1,
|
||||
this.totalPages = 1,
|
||||
});
|
||||
|
||||
CategoriesChildrenState copyWith({
|
||||
List<CategoriesModel>? categories,
|
||||
bool? isLoading,
|
||||
String? errorMessage,
|
||||
int? currentPage,
|
||||
int? totalPages,
|
||||
}) {
|
||||
return CategoriesChildrenState(
|
||||
categories: categories ?? this.categories,
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
errorMessage: errorMessage ?? this.errorMessage,
|
||||
currentPage: currentPage ?? this.currentPage,
|
||||
totalPages: totalPages ?? this.totalPages,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CategoriesChildrenInitial extends CategoriesChildrenState {
|
||||
const CategoriesChildrenInitial()
|
||||
: super(
|
||||
categories: const [],
|
||||
currentPage: 1,
|
||||
errorMessage: null,
|
||||
isLoading: false,
|
||||
totalPages: 1);
|
||||
}
|
||||
|
||||
final class CategoriesChildrenLoading extends CategoriesChildrenState {}
|
||||
|
||||
final class CategoriesChildrenLoaded extends CategoriesChildrenState {
|
||||
CategoriesChildrenLoaded({
|
||||
super.categories,
|
||||
super.isLoading,
|
||||
super.errorMessage,
|
||||
super.currentPage,
|
||||
super.totalPages,
|
||||
});
|
||||
}
|
||||
|
||||
final class CategoriesChildrenError extends CategoriesChildrenState {
|
||||
const CategoriesChildrenError(String errorMessage)
|
||||
: super(errorMessage: errorMessage);
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:proxibuy/data/models/categories/categories_model.dart';
|
||||
import 'package:proxibuy/data/repositories/category_repository.dart';
|
||||
|
||||
part 'categories_state.dart';
|
||||
|
||||
class CategoriesCubit extends Cubit<CategoriesState> {
|
||||
CategoriesCubit() : super(CategoriesInitial());
|
||||
|
||||
void updateCategories(List<CategoriesModel> newCategories) {
|
||||
emit(state.copyWith(categories: newCategories));
|
||||
}
|
||||
|
||||
Future<void> getAllCategories() async {
|
||||
if (state.isLoading || state.currentPage > state.totalPages) return;
|
||||
if (state.currentPage == 1) {
|
||||
emit(CategoriesLoading());
|
||||
} else {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
}
|
||||
try {
|
||||
final response =
|
||||
await CategoryRepository.fetchAll(page: state.currentPage);
|
||||
emit(CategoriesLoaded(
|
||||
categories: List.from(state.categories)..addAll(response.data ?? []),
|
||||
isLoading: false,
|
||||
currentPage: state.currentPage + 1,
|
||||
totalPages: response.meta?.totalPages ?? 1,
|
||||
));
|
||||
} catch (e) {
|
||||
emit(CategoriesError(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future resetPagination() async {
|
||||
emit(CategoriesInitial());
|
||||
}
|
||||
}
|
||||
|
|
@ -4,33 +4,54 @@ class CategoriesState {
|
|||
final List<CategoriesModel> categories;
|
||||
final bool isLoading;
|
||||
final String? errorMessage;
|
||||
final int currentPage;
|
||||
final int totalPages;
|
||||
|
||||
const CategoriesState({
|
||||
this.categories = const [],
|
||||
this.isLoading = false,
|
||||
this.errorMessage,
|
||||
this.currentPage = 1,
|
||||
this.totalPages = 1,
|
||||
});
|
||||
|
||||
CategoriesState copyWith({
|
||||
List<CategoriesModel>? categories,
|
||||
bool? isLoading,
|
||||
String? errorMessage,
|
||||
int? currentPage,
|
||||
int? totalPages,
|
||||
}) {
|
||||
return CategoriesState(
|
||||
categories: categories ?? this.categories,
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
errorMessage: errorMessage ?? this.errorMessage,
|
||||
currentPage: currentPage ?? this.currentPage,
|
||||
totalPages: totalPages ?? this.totalPages,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CategoriesInitial extends CategoriesState {}
|
||||
final class CategoriesInitial extends CategoriesState {
|
||||
const CategoriesInitial()
|
||||
: super(
|
||||
categories: const [],
|
||||
currentPage: 1,
|
||||
errorMessage: null,
|
||||
isLoading: false,
|
||||
totalPages: 1);
|
||||
}
|
||||
|
||||
final class CategoriesLoading extends CategoriesState {}
|
||||
|
||||
final class CategoriesLoaded extends CategoriesState {
|
||||
const CategoriesLoaded(List<CategoriesModel> categories)
|
||||
: super(categories: categories);
|
||||
CategoriesLoaded({
|
||||
super.categories,
|
||||
super.isLoading,
|
||||
super.errorMessage,
|
||||
super.currentPage,
|
||||
super.totalPages,
|
||||
});
|
||||
}
|
||||
|
||||
final class CategoriesError extends CategoriesState {
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:proxibuy/data/models/categories/categories_model.dart';
|
||||
import 'package:proxibuy/data/repositories/category_repository.dart';
|
||||
|
||||
part 'category_state.dart';
|
||||
|
||||
class CategoryCubit extends Cubit<CategoryState> {
|
||||
CategoryCubit() : super(CategoryInitial());
|
||||
|
||||
void getCategory(String id) async {
|
||||
emit(CategoryLoading());
|
||||
try {
|
||||
final response = await CategoryRepository.fetchOne(id: id);
|
||||
emit(CategorySuccess(category: response));
|
||||
} catch (e) {
|
||||
emit(CategoryFail(message: e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
part of 'category_cubit.dart';
|
||||
|
||||
sealed class CategoryState {}
|
||||
|
||||
final class CategoryInitial extends CategoryState {}
|
||||
|
||||
final class CategorySuccess extends CategoryState {
|
||||
final CategoriesModel category;
|
||||
|
||||
CategorySuccess({required this.category});
|
||||
}
|
||||
|
||||
final class CategoryFail extends CategoryState {
|
||||
final String? message;
|
||||
|
||||
CategoryFail({required this.message});
|
||||
}
|
||||
|
||||
final class CategoryLoading extends CategoryState {}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:proxibuy/data/models/categories/categories_model.dart';
|
||||
import 'package:proxibuy/data/repositories/category_repository.dart';
|
||||
|
||||
part 'categories_state.dart';
|
||||
|
||||
class CategoriesCubit extends Cubit<CategoriesState> {
|
||||
CategoriesCubit() : super(CategoriesInitial());
|
||||
|
||||
void updateCategories(List<CategoriesModel> newCategories) {
|
||||
emit(state.copyWith(categories: newCategories));
|
||||
}
|
||||
|
||||
Future<void> getAllCategories() async {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
try {
|
||||
final categories = await CategoryRepository.fetchAll();
|
||||
if (categories != null) {
|
||||
emit(CategoriesLoaded(categories));
|
||||
} else {
|
||||
emit(const CategoriesError('Failed to load categories'));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(CategoriesError(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import 'package:proxibuy/core/gen/assets.gen.dart';
|
|||
import 'package:proxibuy/core/routes/app_router.dart';
|
||||
import 'package:proxibuy/core/utils/empty_space.dart';
|
||||
import 'package:proxibuy/data/models/onboarding_banner_model.dart';
|
||||
import 'package:proxibuy/presentation/providers/cubit/them_mode_cubit.dart';
|
||||
import 'package:proxibuy/presentation/providers/them_mode_cubit.dart';
|
||||
import 'package:proxibuy/presentation/ui/theme/responsive.dart';
|
||||
import 'package:proxibuy/presentation/ui/widgets/carousel/carousel_slider_widget.dart';
|
||||
import 'package:proxibuy/presentation/ui/widgets/edit_text/phone_number_input.dart';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,124 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:proxibuy/presentation/providers/category/cubit/categories_children_cubit.dart';
|
||||
import 'package:proxibuy/presentation/providers/category/cubit/category_cubit.dart';
|
||||
import 'package:proxibuy/presentation/ui/widgets/default_placeholder.dart';
|
||||
|
||||
class CategoriesPage extends StatefulWidget {
|
||||
final String id;
|
||||
const CategoriesPage({super.key, required this.id});
|
||||
|
||||
@override
|
||||
State<CategoriesPage> createState() => _CategoriesPageState();
|
||||
}
|
||||
|
||||
class _CategoriesPageState extends State<CategoriesPage> {
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_scrollController.addListener(_onScroll);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
await context.read<CategoriesChildrenCubit>().resetPagination();
|
||||
if (mounted) {
|
||||
context
|
||||
.read<CategoriesChildrenCubit>()
|
||||
.getAllChildCategories(widget.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _onScroll() {
|
||||
if (_scrollController.position.pixels ==
|
||||
_scrollController.position.maxScrollExtent) {
|
||||
context.read<CategoriesChildrenCubit>().getAllChildCategories(widget.id);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: BlocBuilder<CategoryCubit, CategoryState>(
|
||||
builder: (context, state) {
|
||||
if (state is CategorySuccess) {
|
||||
return Text(state.category.name ?? '...');
|
||||
}
|
||||
return SizedBox();
|
||||
},
|
||||
)),
|
||||
body: BlocBuilder<CategoriesChildrenCubit, CategoriesChildrenState>(
|
||||
builder: (context, state) {
|
||||
if (state is CategoriesChildrenLoaded) {
|
||||
if (state.categories.isEmpty) {
|
||||
return Center(child: Text('Empty'));
|
||||
}
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
ListView.builder(
|
||||
// controller: _scrollController,
|
||||
itemCount: state.categories.length,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, index) {
|
||||
if (index >= state.categories.length) {
|
||||
return Center(child: CircularProgressIndicator());
|
||||
}
|
||||
final category = state.categories[index];
|
||||
return ListTile(
|
||||
leading: SvgPicture.network(
|
||||
'${state.categories[index].url}',
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
placeholderBuilder: (context) => DefaultPlaceHolder(
|
||||
child: Container(
|
||||
width: 24,
|
||||
height: 24,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle, color: Colors.white),
|
||||
)),
|
||||
errorBuilder: (context, error, stackTrace) =>
|
||||
Icon(Icons.category_outlined),
|
||||
),
|
||||
title: Text(category.name ?? ''),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (state.isLoading) LinearProgressIndicator()
|
||||
],
|
||||
),
|
||||
);
|
||||
} else if (state is CategoriesChildrenError) {
|
||||
return Center(child: Text(state.errorMessage!));
|
||||
} else {
|
||||
return ListView.builder(
|
||||
// controller: _scrollController,
|
||||
itemCount: 20,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, index) {
|
||||
return DefaultPlaceHolder(
|
||||
child: Container(
|
||||
margin: EdgeInsets.symmetric(vertical: 8),
|
||||
color: Colors.white,
|
||||
child: ListTile(
|
||||
leading: Icon(Icons.abc_outlined),
|
||||
title: Text(''),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
|
@ -6,9 +6,10 @@ import 'package:proxibuy/core/gen/assets.gen.dart';
|
|||
import 'package:proxibuy/core/routes/app_router.dart';
|
||||
import 'package:proxibuy/core/utils/empty_space.dart';
|
||||
import 'package:proxibuy/data/models/screen_model.dart';
|
||||
import 'package:proxibuy/presentation/providers/cubit/them_mode_cubit.dart';
|
||||
import 'package:proxibuy/presentation/providers/them_mode_cubit.dart';
|
||||
import 'package:proxibuy/presentation/ui/theme/responsive.dart';
|
||||
import 'package:proxibuy/presentation/ui/theme/theme.dart';
|
||||
import 'package:proxibuy/presentation/ui/widgets/navigations/categories_mega_menu.dart';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
final Widget child;
|
||||
|
|
@ -55,65 +56,70 @@ class _HomePageState extends State<HomePage> {
|
|||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SelectableText(
|
||||
"Proxibuy",
|
||||
style: Theme.of(context).textTheme.displaySmall,
|
||||
),
|
||||
32.w,
|
||||
Row(
|
||||
children: [
|
||||
...List.generate(
|
||||
screens.length,
|
||||
(index) => Row(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: selectedIndex == index
|
||||
? themeColor(context)
|
||||
?.primaryLightSurface
|
||||
.withAlpha(90)
|
||||
: null),
|
||||
child: IconButton(
|
||||
splashRadius: 12,
|
||||
onPressed: () {
|
||||
_onItemTapped(context, index);
|
||||
},
|
||||
icon: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
screens[index].icon.svg(
|
||||
color: selectedIndex == index
|
||||
? Theme.of(context).primaryColor
|
||||
: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface),
|
||||
12.w,
|
||||
Text(
|
||||
screens[index].title,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelLarge
|
||||
?.copyWith(
|
||||
color: selectedIndex == index
|
||||
? Theme.of(context).primaryColor
|
||||
: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface),
|
||||
)
|
||||
],
|
||||
)),
|
||||
),
|
||||
24.w
|
||||
],
|
||||
),
|
||||
),
|
||||
CategoriesMegaMenu()
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Flexible(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
SelectableText(
|
||||
"Proxibuy",
|
||||
style: Theme.of(context).textTheme.displaySmall,
|
||||
),
|
||||
32.w,
|
||||
Row(
|
||||
children: [
|
||||
...List.generate(
|
||||
screens.length,
|
||||
(index) => Row(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: selectedIndex == index
|
||||
? themeColor(context)
|
||||
?.primaryLightSurface
|
||||
.withAlpha(90)
|
||||
: null),
|
||||
child: IconButton(
|
||||
splashRadius: 12,
|
||||
onPressed: () {
|
||||
_onItemTapped(context, index);
|
||||
},
|
||||
icon: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
screens[index].icon.svg(
|
||||
color: selectedIndex == index
|
||||
? Theme.of(context).primaryColor
|
||||
: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface),
|
||||
12.w,
|
||||
Text(
|
||||
screens[index].title,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelLarge
|
||||
?.copyWith(
|
||||
color: selectedIndex == index
|
||||
? Theme.of(context)
|
||||
.primaryColor
|
||||
: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface),
|
||||
)
|
||||
],
|
||||
)),
|
||||
),
|
||||
24.w
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
8.w,
|
||||
Flexible(
|
||||
child: Container(
|
||||
|
|
@ -133,24 +139,20 @@ class _HomePageState extends State<HomePage> {
|
|||
),
|
||||
)),
|
||||
32.w,
|
||||
IconButton(
|
||||
icon: Assets.icon.outline.notificationBing
|
||||
.svg(color: Theme.of(context).colorScheme.onSurface),
|
||||
onPressed: () {},
|
||||
),
|
||||
12.w,
|
||||
IconButton(
|
||||
icon: Icon(Icons.brightness_6),
|
||||
onPressed: () {
|
||||
context.read<ThemModeCubit>().changeTheme();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Assets.icon.outline.notificationBing
|
||||
.svg(color: Theme.of(context).colorScheme.onSurface),
|
||||
onPressed: () {},
|
||||
),
|
||||
12.w,
|
||||
IconButton(
|
||||
icon: Icon(Icons.brightness_6),
|
||||
onPressed: () {
|
||||
context.read<ThemModeCubit>().changeTheme();
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:proxibuy/core/gen/assets.gen.dart';
|
||||
import 'package:proxibuy/core/routes/app_router.dart';
|
||||
import 'package:proxibuy/core/utils/empty_space.dart';
|
||||
import 'package:proxibuy/presentation/providers/cubit/them_mode_cubit.dart';
|
||||
import 'package:proxibuy/presentation/providers/cubit/categories_cubit.dart';
|
||||
import 'package:proxibuy/presentation/providers/them_mode_cubit.dart';
|
||||
import 'package:proxibuy/presentation/providers/category/cubit/categories_cubit.dart';
|
||||
import 'package:proxibuy/presentation/ui/theme/colors.dart';
|
||||
import 'package:proxibuy/presentation/ui/theme/responsive.dart';
|
||||
import 'package:proxibuy/presentation/ui/widgets/carousel/carousel_slider_widget.dart';
|
||||
import 'package:proxibuy/presentation/ui/widgets/default_placeholder.dart';
|
||||
|
||||
class HomeScreen extends StatefulWidget {
|
||||
const HomeScreen({super.key});
|
||||
|
|
@ -52,48 +54,8 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||
child: Column(
|
||||
children: [
|
||||
12.h,
|
||||
titleDivider(context, title: 'what\'s on your mind?', top: 16),
|
||||
BlocBuilder<CategoriesCubit, CategoriesState>(
|
||||
builder: (context, state) {
|
||||
if (state is CategoriesLoaded) {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: 40,
|
||||
child: ListView.builder(
|
||||
itemCount: state.categories.length,
|
||||
scrollDirection: Axis.horizontal,
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
physics: BouncingScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
return Container(
|
||||
width: 200,
|
||||
height: 40,
|
||||
margin: EdgeInsets.symmetric(horizontal: 6),
|
||||
padding: EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.car_crash),
|
||||
8.w,
|
||||
Text(state.categories[index].name ?? '')
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
} else if (state is CategoriesError) {
|
||||
return Text('Failed to load categories');
|
||||
} else {
|
||||
return CircularProgressIndicator();
|
||||
}
|
||||
},
|
||||
),
|
||||
// categories(context),
|
||||
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
|
@ -633,9 +595,13 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||
BlocBuilder<CategoriesCubit, CategoriesState>(
|
||||
builder: (context, state) {
|
||||
if (state is CategoriesLoaded) {
|
||||
if (state.categories.isEmpty) {
|
||||
return Text('Empty');
|
||||
}
|
||||
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: 64,
|
||||
height: Responsive(context).isMobile() ? 64 : 40,
|
||||
child: ListView.builder(
|
||||
itemCount: state.categories.length,
|
||||
scrollDirection: Axis.horizontal,
|
||||
|
|
@ -643,17 +609,54 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
physics: BouncingScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
return Container(
|
||||
width: 64,
|
||||
height: 64,
|
||||
margin: EdgeInsets.symmetric(horizontal: 6),
|
||||
padding: EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(Icons.abc),
|
||||
ValueNotifier<bool> isHovered = ValueNotifier(false);
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 6),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
context.go(
|
||||
'${AppRouter.categories}/${state.categories[index].id}');
|
||||
},
|
||||
onHover: (value) {
|
||||
isHovered.value = value;
|
||||
},
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: isHovered,
|
||||
builder: (context, hovered, _) {
|
||||
return Container(
|
||||
width:
|
||||
Responsive(context).isMobile() ? 64 : 200,
|
||||
height:
|
||||
Responsive(context).isMobile() ? 64 : 40,
|
||||
padding: EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: hovered
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.network(
|
||||
'${state.categories[index].url}',
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface,
|
||||
placeholderBuilder: (context) =>
|
||||
CircularProgressIndicator(),
|
||||
errorBuilder:
|
||||
(context, error, stackTrace) =>
|
||||
Icon(Icons.error),
|
||||
),
|
||||
if (!Responsive(context).isMobile()) ...[
|
||||
8.w,
|
||||
Text(state.categories[index].name ?? '')
|
||||
]
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
@ -662,7 +665,30 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||
} else if (state is CategoriesError) {
|
||||
return Text('Failed to load categories');
|
||||
} else {
|
||||
return CircularProgressIndicator();
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: Responsive(context).isMobile() ? 64 : 40,
|
||||
child: ListView.builder(
|
||||
itemCount: 10,
|
||||
scrollDirection: Axis.horizontal,
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
return DefaultPlaceHolder(
|
||||
child: Container(
|
||||
width: Responsive(context).isMobile() ? 64 : 200,
|
||||
height: Responsive(context).isMobile() ? 64 : 40,
|
||||
padding: EdgeInsets.all(8),
|
||||
margin: EdgeInsets.symmetric(horizontal: 6),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -4,11 +4,15 @@ class CustomColors extends ThemeExtension<CustomColors> {
|
|||
final MaterialColor primarySwatch;
|
||||
final MaterialColor secondrySwatch;
|
||||
final Color primaryLightSurface;
|
||||
final Color baseColor;
|
||||
final Color highlightColor;
|
||||
|
||||
CustomColors({
|
||||
required this.primarySwatch,
|
||||
required this.secondrySwatch,
|
||||
required this.primaryLightSurface,
|
||||
required this.baseColor,
|
||||
required this.highlightColor,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -16,23 +20,30 @@ class CustomColors extends ThemeExtension<CustomColors> {
|
|||
MaterialColor? primarySwatch,
|
||||
MaterialColor? secondrySwatch,
|
||||
Color? primaryLightSurface,
|
||||
Color? baseColor,
|
||||
Color? highlightColor,
|
||||
}) {
|
||||
return CustomColors(
|
||||
primarySwatch: primarySwatch ?? this.primarySwatch,
|
||||
secondrySwatch: secondrySwatch ?? this.secondrySwatch,
|
||||
primaryLightSurface: primaryLightSurface ?? this.primaryLightSurface,
|
||||
baseColor: baseColor ?? this.baseColor,
|
||||
highlightColor: highlightColor ?? this.highlightColor,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
ThemeExtension<CustomColors> lerp(
|
||||
ThemeExtension<CustomColors>? other, double t) {
|
||||
if (other is! CustomColors) return this;
|
||||
CustomColors lerp(ThemeExtension<CustomColors>? other, double t) {
|
||||
if (other is! CustomColors) {
|
||||
return this;
|
||||
}
|
||||
return CustomColors(
|
||||
primarySwatch: other.primarySwatch,
|
||||
secondrySwatch: other.secondrySwatch,
|
||||
primaryLightSurface:
|
||||
Color.lerp(primaryLightSurface, other.primaryLightSurface, t)!,
|
||||
baseColor: Color.lerp(baseColor, other.baseColor, t)!,
|
||||
highlightColor: Color.lerp(highlightColor, other.highlightColor, t)!,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ final ThemeData appTheme = ThemeData(
|
|||
primarySwatch: primarySwatch,
|
||||
secondrySwatch: secondarySwatch,
|
||||
primaryLightSurface: primarySwatch[200]!,
|
||||
baseColor: Colors.grey[300]!,
|
||||
highlightColor: Colors.grey[100]!,
|
||||
),
|
||||
],
|
||||
|
||||
|
|
@ -91,6 +93,8 @@ final ThemeData darkTheme = ThemeData(
|
|||
primarySwatch: darkPrimarySwatch,
|
||||
secondrySwatch: darkSecondarySwatch,
|
||||
primaryLightSurface: darkPrimarySwatch[600]!,
|
||||
baseColor: Colors.grey[700]!,
|
||||
highlightColor: Colors.grey[500]!,
|
||||
),
|
||||
],
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:shimmer/shimmer.dart';
|
||||
import 'package:proxibuy/presentation/ui/theme/theme.dart';
|
||||
|
||||
class DefaultPlaceHolder extends StatelessWidget {
|
||||
final Widget child;
|
||||
final bool enabled;
|
||||
final double? width;
|
||||
final double? height;
|
||||
const DefaultPlaceHolder(
|
||||
{super.key,
|
||||
required this.child,
|
||||
this.enabled = true,
|
||||
this.width,
|
||||
this.height});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colors = themeColor(context);
|
||||
return enabled
|
||||
? IgnorePointer(
|
||||
ignoring: true,
|
||||
child: SizedBox(
|
||||
width: width,
|
||||
height: height,
|
||||
child: Shimmer.fromColors(
|
||||
baseColor: colors?.baseColor ?? Colors.grey[300]!,
|
||||
highlightColor: colors?.highlightColor ?? Colors.grey[100]!,
|
||||
child: child),
|
||||
),
|
||||
)
|
||||
: child;
|
||||
}
|
||||
}
|
||||
|
|
@ -46,18 +46,6 @@ class ImageGallary extends ModalRoute<void> {
|
|||
|
||||
late final ValueNotifier<String> initialImage = ValueNotifier(initialUrl);
|
||||
Widget _buildOverlayContent(BuildContext context) {
|
||||
double x = 1;
|
||||
if (MediaQuery.sizeOf(context).width < 400) {
|
||||
x = 0.27;
|
||||
} else if (MediaQuery.sizeOf(context).width < 600) {
|
||||
x = 0.2;
|
||||
} else if (Responsive(context).isMobile()) {
|
||||
x = 0.5;
|
||||
} else if (Responsive(context).isTablet()) {
|
||||
x = 1;
|
||||
} else {
|
||||
x = 1.5;
|
||||
}
|
||||
double viewportFraction =
|
||||
(1 / (urls.length * (MediaQuery.sizeOf(context).width / 1300)));
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,89 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class HoverSubmenu extends StatefulWidget {
|
||||
final String title;
|
||||
final List<String> submenuItems;
|
||||
|
||||
const HoverSubmenu(
|
||||
{super.key, required this.title, required this.submenuItems});
|
||||
|
||||
@override
|
||||
State<HoverSubmenu> createState() => _HoverSubmenuState();
|
||||
}
|
||||
|
||||
class _HoverSubmenuState extends State<HoverSubmenu> {
|
||||
OverlayEntry? _submenuOverlay;
|
||||
final LayerLink _submenuLink = LayerLink();
|
||||
|
||||
void _showSubmenu(BuildContext context) {
|
||||
_submenuOverlay = _createSubmenu();
|
||||
Overlay.of(context).insert(_submenuOverlay!);
|
||||
}
|
||||
|
||||
void _hideSubmenu() {
|
||||
_submenuOverlay?.remove();
|
||||
_submenuOverlay = null;
|
||||
}
|
||||
|
||||
OverlayEntry _createSubmenu() {
|
||||
return OverlayEntry(
|
||||
builder: (context) => Positioned(
|
||||
left: 250, // Adjust submenu position
|
||||
top: 0,
|
||||
child: CompositedTransformFollower(
|
||||
link: _submenuLink,
|
||||
offset: Offset(200, 0),
|
||||
child: MouseRegion(
|
||||
onExit: (_) => _hideSubmenu(),
|
||||
child: Material(
|
||||
elevation: 4,
|
||||
color: Colors.white,
|
||||
child: Container(
|
||||
width: 200,
|
||||
padding: EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 4)],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: widget.submenuItems
|
||||
.map((item) => ListTile(
|
||||
title: Text(item),
|
||||
onTap: () {
|
||||
_hideSubmenu();
|
||||
print('$item clicked');
|
||||
},
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CompositedTransformTarget(
|
||||
link: _submenuLink,
|
||||
child: MouseRegion(
|
||||
onEnter: (_) => _showSubmenu(context),
|
||||
child: ListTile(
|
||||
title: Text(widget.title),
|
||||
trailing: Icon(Icons.arrow_right),
|
||||
onTap: () {},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_submenuOverlay?.remove();
|
||||
_submenuOverlay = null;
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,257 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:proxibuy/core/utils/empty_space.dart';
|
||||
import 'package:proxibuy/presentation/providers/category/cubit/categories_children_cubit.dart';
|
||||
import 'package:proxibuy/presentation/providers/category/cubit/categories_cubit.dart';
|
||||
import 'package:proxibuy/presentation/ui/theme/colors.dart';
|
||||
import 'package:proxibuy/presentation/ui/theme/theme.dart';
|
||||
|
||||
class CategoriesMegaMenu extends StatefulWidget {
|
||||
const CategoriesMegaMenu({super.key});
|
||||
|
||||
@override
|
||||
State<CategoriesMegaMenu> createState() => _CategoriesMegaMenuState();
|
||||
}
|
||||
|
||||
class _CategoriesMegaMenuState extends State<CategoriesMegaMenu> {
|
||||
OverlayEntry? _overlayEntry;
|
||||
final LayerLink _layerLink = LayerLink();
|
||||
ValueNotifier<bool> onHovered = ValueNotifier(false);
|
||||
late ValueNotifier<String?> selectedId = ValueNotifier(null);
|
||||
bool visibleMenu = false;
|
||||
void _showMegaMenu() {
|
||||
_overlayEntry = _createMegaMenu();
|
||||
Overlay.of(context).insert(_overlayEntry!);
|
||||
visibleMenu = true;
|
||||
}
|
||||
|
||||
void _hideMegaMenu() {
|
||||
_overlayEntry?.remove();
|
||||
_overlayEntry = null;
|
||||
visibleMenu = false;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
selectedId.value =
|
||||
context.read<CategoriesCubit>().state.categories.first.id;
|
||||
});
|
||||
|
||||
context.read<CategoriesCubit>().stream.listen(
|
||||
(state) {
|
||||
if (!state.isLoading && state.categories.isNotEmpty) {
|
||||
selectedId.value = state.categories.first.id!;
|
||||
if (mounted) {
|
||||
context
|
||||
.read<CategoriesChildrenCubit>()
|
||||
.getAllChildCategories(selectedId.value!);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_hideMegaMenu();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
OverlayEntry _createMegaMenu() {
|
||||
return OverlayEntry(
|
||||
builder: (context) => Positioned(
|
||||
width: 800, // Adjust width of the mega menu
|
||||
child: CompositedTransformFollower(
|
||||
link: _layerLink,
|
||||
offset: Offset(0, 50), // Adjust position below the button
|
||||
child: MouseRegion(
|
||||
onExit: (_) => _hideMegaMenu(),
|
||||
child: Material(
|
||||
elevation: 4,
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 4)],
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: BlocBuilder<CategoriesCubit, CategoriesState>(
|
||||
builder: (context, state) {
|
||||
if (state.isLoading && state.categories.isEmpty) {
|
||||
return Center(child: CircularProgressIndicator());
|
||||
} else if (state.errorMessage != null) {
|
||||
return Center(child: Text(state.errorMessage!));
|
||||
} else {
|
||||
return SingleChildScrollView(
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: selectedId,
|
||||
builder: (context, id, _) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
...List.generate(
|
||||
context
|
||||
.read<CategoriesCubit>()
|
||||
.state
|
||||
.categories
|
||||
.length,
|
||||
(index) {
|
||||
final cat = context
|
||||
.read<CategoriesCubit>()
|
||||
.state
|
||||
.categories[index];
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
if (cat.id != null) {
|
||||
selectedId.value = cat.id!;
|
||||
context
|
||||
.read<
|
||||
CategoriesChildrenCubit>()
|
||||
.resetPagination();
|
||||
context
|
||||
.read<
|
||||
CategoriesChildrenCubit>()
|
||||
.getAllChildCategories(
|
||||
id!);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
color: cat.id == id
|
||||
? semanticBlue
|
||||
: null,
|
||||
child: _menuItem(
|
||||
cat.name ?? '')));
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: BlocBuilder<CategoriesChildrenCubit,
|
||||
CategoriesChildrenState>(
|
||||
builder: (context, state) {
|
||||
if (state.isLoading && state.categories.isEmpty) {
|
||||
return Center(child: CircularProgressIndicator());
|
||||
} else if (state.errorMessage != null) {
|
||||
return Center(child: Text(state.errorMessage!));
|
||||
} else {
|
||||
return NotificationListener<ScrollNotification>(
|
||||
onNotification: (scrollNotification) {
|
||||
if (scrollNotification.metrics.pixels ==
|
||||
scrollNotification
|
||||
.metrics.maxScrollExtent) {
|
||||
context
|
||||
.read<CategoriesChildrenCubit>()
|
||||
.getAllChildCategories(selectedId.value!);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: 300), // Adjust height as needed
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: state.categories.length +
|
||||
(state.isLoading ? 1 : 0),
|
||||
itemBuilder: (context, index) {
|
||||
if (index >= state.categories.length) {
|
||||
return Center(
|
||||
child: CircularProgressIndicator());
|
||||
}
|
||||
final category = state.categories[index];
|
||||
return ListTile(
|
||||
title: Text(category.name ?? ''),
|
||||
onTap: () {
|
||||
print('${category.name} clicked');
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _menuItem(String title) {
|
||||
return ListTile(
|
||||
title: Text(title),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CompositedTransformTarget(
|
||||
link: _layerLink,
|
||||
child: InkWell(
|
||||
onTap: () {},
|
||||
onHover: (value) {
|
||||
onHovered.value = value;
|
||||
},
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: onHovered,
|
||||
builder: (context, hovered, _) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: hovered
|
||||
? themeColor(context)?.primaryLightSurface.withAlpha(90)
|
||||
: null),
|
||||
child: IconButton(
|
||||
splashRadius: 12,
|
||||
onPressed: () {
|
||||
if (visibleMenu) {
|
||||
_hideMegaMenu();
|
||||
} else {
|
||||
_showMegaMenu();
|
||||
}
|
||||
},
|
||||
icon: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Icon(Icons.category,
|
||||
color: hovered
|
||||
? Theme.of(context).primaryColor
|
||||
: Theme.of(context).colorScheme.onSurface),
|
||||
12.w,
|
||||
Text(
|
||||
'Categories',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelLarge
|
||||
?.copyWith(
|
||||
color: hovered
|
||||
? Theme.of(context).primaryColor
|
||||
: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface),
|
||||
)
|
||||
],
|
||||
)),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,264 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:ll_dropdown_menu/ll_dropdown_menu.dart';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
/// Wrapper AppBar for customizing default values
|
||||
class WrapperAppBar extends AppBar {
|
||||
static Widget? _defaultLeading;
|
||||
static Widget? _defaultTitle;
|
||||
static TextStyle? _defaultToolbarTextStyle;
|
||||
static TextStyle? _defaultTitleTextStyle;
|
||||
static double? _defaultToolbarHeight;
|
||||
static SystemUiOverlayStyle? _defaultSystemOverlayStyle;
|
||||
|
||||
static void initConfig({
|
||||
Widget? defaultLeading,
|
||||
Widget? defaultTitle,
|
||||
TextStyle? defaultToolbarTextStyle,
|
||||
TextStyle? defaultTitleTextStyle,
|
||||
double? defaultToolbarHeight,
|
||||
SystemUiOverlayStyle? defaultSystemOverlayStyle,
|
||||
}) {
|
||||
_defaultLeading = defaultLeading;
|
||||
_defaultTitle = defaultTitle;
|
||||
_defaultToolbarTextStyle = defaultToolbarTextStyle;
|
||||
_defaultTitleTextStyle = defaultTitleTextStyle;
|
||||
_defaultToolbarHeight = defaultToolbarHeight;
|
||||
_defaultSystemOverlayStyle = defaultSystemOverlayStyle;
|
||||
}
|
||||
|
||||
WrapperAppBar({
|
||||
super.key,
|
||||
Widget? leading,
|
||||
super.automaticallyImplyLeading,
|
||||
Widget? title,
|
||||
super.actions,
|
||||
super.flexibleSpace,
|
||||
super.bottom,
|
||||
double super.elevation = 0,
|
||||
super.scrolledUnderElevation,
|
||||
super.notificationPredicate,
|
||||
super.shadowColor,
|
||||
super.surfaceTintColor,
|
||||
super.shape,
|
||||
Color super.backgroundColor = Colors.white,
|
||||
super.foregroundColor,
|
||||
super.iconTheme,
|
||||
super.actionsIconTheme,
|
||||
super.primary,
|
||||
bool super.centerTitle = true,
|
||||
super.excludeHeaderSemantics,
|
||||
super.titleSpacing,
|
||||
super.toolbarOpacity,
|
||||
super.bottomOpacity,
|
||||
double? toolbarHeight,
|
||||
super.leadingWidth,
|
||||
TextStyle? toolbarTextStyle,
|
||||
TextStyle? titleTextStyle,
|
||||
SystemUiOverlayStyle? systemOverlayStyle,
|
||||
super.forceMaterialTransparency,
|
||||
super.clipBehavior,
|
||||
String? titleText,
|
||||
}) : super(
|
||||
leading:
|
||||
leading ?? (automaticallyImplyLeading ? _defaultLeading : null),
|
||||
title: title ??
|
||||
_defaultTitle ??
|
||||
Text(titleText ?? '', style: titleTextStyle),
|
||||
toolbarHeight: toolbarHeight ?? _defaultToolbarHeight,
|
||||
toolbarTextStyle: toolbarTextStyle ?? _defaultToolbarTextStyle,
|
||||
titleTextStyle: titleTextStyle ??
|
||||
_defaultTitleTextStyle ??
|
||||
const TextStyle(color: Colors.black, fontSize: 18),
|
||||
systemOverlayStyle: systemOverlayStyle ?? _defaultSystemOverlayStyle,
|
||||
);
|
||||
}
|
||||
|
||||
class DropDownDemo3 extends StatefulWidget {
|
||||
const DropDownDemo3({super.key});
|
||||
|
||||
@override
|
||||
State<DropDownDemo3> createState() => _DropDownDemoState();
|
||||
}
|
||||
|
||||
class _DropDownDemoState extends State<DropDownDemo3>
|
||||
with SingleTickerProviderStateMixin {
|
||||
final DropDownController dropDownController = DropDownController();
|
||||
final DropDownCascadeListDataController dataController1 =
|
||||
DropDownCascadeListDataController();
|
||||
final DropDownCascadeListDataController dataController2 =
|
||||
DropDownCascadeListDataController();
|
||||
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
|
||||
|
||||
List<DropDownItem<List<DropDownItem>>> data1 = List.generate(
|
||||
6,
|
||||
(index) => DropDownItem<List<DropDownItem>>(
|
||||
text: "Tab $index",
|
||||
data: List.generate(
|
||||
index + 2,
|
||||
(index) => DropDownItem(
|
||||
text: "Tab Second $index",
|
||||
activeIcon: const Icon(Icons.check),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
List<DropDownItem<List<DropDownItem>>> data2 = List.generate(
|
||||
6,
|
||||
(index) => DropDownItem<List<DropDownItem>>(
|
||||
text: "Tab $index",
|
||||
data: List.generate(
|
||||
index + 2,
|
||||
(index) => DropDownItem(
|
||||
text: "Tab Second $index",
|
||||
activeIcon: const Icon(Icons.check),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
key: scaffoldKey,
|
||||
appBar: WrapperAppBar(
|
||||
titleText: "DropDownDemo Custom",
|
||||
backgroundColor: Colors.white,
|
||||
actions: const <Widget>[
|
||||
SizedBox(),
|
||||
],
|
||||
),
|
||||
body: Column(children: [
|
||||
DropDownHeader(
|
||||
controller: dropDownController,
|
||||
boxStyle: const DropDownBoxStyle(
|
||||
height: 50,
|
||||
backgroundColor: Colors.white,
|
||||
margin: EdgeInsets.symmetric(horizontal: 10),
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
),
|
||||
itemStyle: DropDownItemStyle(
|
||||
activeIconColor: Colors.blue,
|
||||
activeTextStyle: const TextStyle(color: Colors.blue),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade100,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
activeDecoration: BoxDecoration(
|
||||
color: Colors.grey.shade100,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 10),
|
||||
alignment: Alignment.center,
|
||||
highlightTextStyle: const TextStyle(color: Colors.white),
|
||||
highlightDecoration: BoxDecoration(
|
||||
color: Colors.black,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
highlightIcon: const Icon(
|
||||
Icons.arrow_drop_down,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
items: List.generate(
|
||||
3,
|
||||
(index) => DropDownItem<Tab>(
|
||||
text: index == 2 ? "Filter" : "Tab $index",
|
||||
icon: index == 2
|
||||
? const Icon(Icons.filter_alt)
|
||||
: const Icon(Icons.arrow_drop_down),
|
||||
activeIcon: index == 2
|
||||
? const Icon(Icons.filter_alt)
|
||||
: const Icon(Icons.arrow_drop_up),
|
||||
),
|
||||
),
|
||||
onItemTap: (index, item) {
|
||||
if (index == 2) {
|
||||
dropDownController.hide();
|
||||
scaffoldKey.currentState?.openEndDrawer();
|
||||
} else {
|
||||
dropDownController.toggle(index);
|
||||
}
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
color: Colors.blue[200],
|
||||
child: Center(
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
dataController1.resetAllItemsStatus();
|
||||
dataController2.resetAllItemsStatus();
|
||||
for (int i = 0; i < 2; i++) {
|
||||
dropDownController.hide(
|
||||
index: i,
|
||||
status: DropDownHeaderStatus(text: "Tab$i"),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: const Text("Reset"),
|
||||
),
|
||||
),
|
||||
),
|
||||
DropDownView(
|
||||
controller: dropDownController,
|
||||
builders: [
|
||||
DropDownCascadeList(
|
||||
controller: dropDownController,
|
||||
dataController: dataController1,
|
||||
headerIndex: 0,
|
||||
secondFloorItemStyle: const DropDownItemStyle(
|
||||
backgroundColor: Colors.white,
|
||||
activeBackgroundColor: Color(0xFFF5F5F5),
|
||||
activeTextStyle:
|
||||
TextStyle(fontSize: 14, color: Colors.blue),
|
||||
activeIconColor: Colors.blue,
|
||||
padding: EdgeInsets.symmetric(horizontal: 20),
|
||||
alignment: Alignment.centerLeft,
|
||||
textExpand: true,
|
||||
),
|
||||
items: data1,
|
||||
),
|
||||
DropDownCascadeList(
|
||||
controller: dropDownController,
|
||||
dataController: dataController2,
|
||||
headerIndex: 1,
|
||||
secondFloorItemStyle: const DropDownItemStyle(
|
||||
backgroundColor: Colors.white,
|
||||
activeBackgroundColor: Color(0xFFF5F5F5),
|
||||
activeTextStyle:
|
||||
TextStyle(fontSize: 14, color: Colors.blue),
|
||||
activeIconColor: Colors.blue,
|
||||
padding: EdgeInsets.symmetric(horizontal: 20),
|
||||
alignment: Alignment.centerLeft,
|
||||
textExpand: true,
|
||||
),
|
||||
items: data2,
|
||||
maxMultiChoiceSize: 3,
|
||||
),
|
||||
DropDownViewWrapper(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: 300,
|
||||
child: Container(
|
||||
color: Colors.yellow,
|
||||
height: 300,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
dropDownController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class MyMegaMenu extends StatelessWidget {
|
||||
const MyMegaMenu({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 300,
|
||||
height: 120,
|
||||
child: DropdownButton<String>(
|
||||
value: 'Menu',
|
||||
icon: Icon(Icons.av_timer),
|
||||
iconSize: 24,
|
||||
elevation: 16,
|
||||
style: TextStyle(color: Colors.deepPurple),
|
||||
underline: Container(
|
||||
height: 2,
|
||||
color: Colors.deepPurpleAccent,
|
||||
),
|
||||
onChanged: (newValue) {},
|
||||
items: <String>['Menu', 'Home', 'Profile', 'Settings']
|
||||
.map<DropdownMenuItem<String>>((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value),
|
||||
);
|
||||
}).toList()
|
||||
..add(DropdownMenuItem(
|
||||
child: ExpansionTile(
|
||||
title: Text('Menu'),
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text('Submenu 1'),
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Submenu 2'),
|
||||
),
|
||||
],
|
||||
),
|
||||
))),
|
||||
);
|
||||
}
|
||||
}
|
||||
16
pubspec.lock
16
pubspec.lock
|
|
@ -544,6 +544,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.1"
|
||||
ll_dropdown_menu:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: ll_dropdown_menu
|
||||
sha256: ae752c626b8207a86479013efce9b7a3181ac9eb864756f1071934bec2d954db
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -832,6 +840,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
shimmer:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shimmer
|
||||
sha256: "5f88c883a22e9f9f299e5ba0e4f7e6054857224976a5d9f839d4ebdc94a14ac9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ dependencies:
|
|||
pretty_dio_logger: ^1.4.0
|
||||
universal_html: ^2.2.4
|
||||
dropdown_textfield: ^1.2.0
|
||||
ll_dropdown_menu: ^0.8.0
|
||||
shimmer: ^3.0.0
|
||||
|
||||
flutter_launcher_icons:
|
||||
android: true
|
||||
|
|
|
|||
Loading…
Reference in New Issue