check and fix in ui
This commit is contained in:
@@ -6,6 +6,7 @@ import 'package:tm_app/data/repositories/tender_detail_repository.dart';
|
||||
import 'package:tm_app/data/services/home_service.dart';
|
||||
import 'package:tm_app/data/services/tender_detail_service.dart';
|
||||
import 'package:tm_app/view_models/tender_detail_view_model.dart';
|
||||
import 'package:tm_app/view_models/tenders_view_model.dart';
|
||||
|
||||
import '../../data/repositories/auth_repository.dart';
|
||||
import '../../data/services/auth_service.dart';
|
||||
@@ -59,6 +60,7 @@ List<SingleChildWidget> get viewModels {
|
||||
(context) =>
|
||||
TenderDetailViewModel(tenderDetailRepository: context.read()),
|
||||
),
|
||||
ChangeNotifierProvider(create: (context) => TendersViewModel()),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,11 @@ class AssetsManager {
|
||||
|
||||
//bottom navigation
|
||||
static const homeActive = 'assets/icons/home_active.svg';
|
||||
static const home = 'assets/icons/home.svg';
|
||||
static const tenders = 'assets/icons/task_square.svg';
|
||||
static const tendersActive = 'assets/icons/task-square_active.svg';
|
||||
static const profile = 'assets/icons/profile-circle.svg';
|
||||
static const profileActive = 'assets/icons/profile-circle_active.svg';
|
||||
|
||||
// login page
|
||||
static const logo = 'assets/pngs/logo.png';
|
||||
@@ -30,6 +35,9 @@ class AssetsManager {
|
||||
static const shield = 'assets/icons/shield.svg';
|
||||
static const edit = 'assets/icons/edit.svg';
|
||||
static const arrowRight = 'assets/icons/arrow-right.svg';
|
||||
static const notification = 'assets/icons/notification.svg';
|
||||
static const calendar = 'assets/icons/calendar.svg';
|
||||
static const menu = 'assets/icons/menu.svg';
|
||||
|
||||
//your tenders page
|
||||
static const tickCircle = 'assets/icons/tick-circle.svg';
|
||||
|
||||
@@ -32,6 +32,7 @@ class AppStrings {
|
||||
static const String tenderValue = 'Tender Value';
|
||||
static const String thunderStatus = 'Thunder status';
|
||||
static const String yourTenders = 'Your tenders';
|
||||
static const String welcome = 'Welcome';
|
||||
|
||||
//Tenders
|
||||
static const String tendersTitle = 'Tenders';
|
||||
|
||||
@@ -4,10 +4,10 @@ import 'package:tm_app/views/detail/pages/detail_screen.dart';
|
||||
import 'package:tm_app/views/home/pages/home_screen.dart';
|
||||
import 'package:tm_app/views/login/pages/login_screen.dart';
|
||||
|
||||
import '../../views/profile/profile_screen.dart';
|
||||
import '../../views/tenders/tenders_screen.dart';
|
||||
import '../../views/profile/pages/profile_screen.dart';
|
||||
import '../../views/tenders/pages/tenders_screen.dart';
|
||||
import '../../views/your_tenders/your_tenders_screen.dart';
|
||||
import 'app_shell_screen.dart';
|
||||
import 'app_shell/app_shell_screen.dart';
|
||||
|
||||
part 'app_routes.g.dart';
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:tm_app/core/routes/app_shell/desktop_shell_page.dart';
|
||||
import 'package:tm_app/core/routes/app_shell/mobile_shell_page.dart';
|
||||
import 'package:tm_app/core/routes/app_shell/tablet_shell_page.dart';
|
||||
import 'package:tm_app/views/shared/responsive_builder.dart';
|
||||
|
||||
class AppShellScreen extends StatelessWidget {
|
||||
const AppShellScreen({required this.navigationShell, super.key});
|
||||
|
||||
final StatefulNavigationShell navigationShell;
|
||||
|
||||
void _gotoBranch(int index) {
|
||||
navigationShell.goBranch(
|
||||
index,
|
||||
initialLocation: index == navigationShell.currentIndex,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(
|
||||
mobile: MobileShellPage(
|
||||
navigationShell: navigationShell,
|
||||
onTap: _gotoBranch,
|
||||
),
|
||||
tablet: TabletShellPage(
|
||||
navigationShell: navigationShell,
|
||||
onTap: _gotoBranch,
|
||||
),
|
||||
desktop: DesktopShellPage(
|
||||
navigationShell: navigationShell,
|
||||
onTap: _gotoBranch,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:tm_app/core/constants/assets.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
|
||||
class DesktopShellPage extends StatelessWidget {
|
||||
const DesktopShellPage({
|
||||
required this.navigationShell,
|
||||
required this.onTap,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final StatefulNavigationShell navigationShell;
|
||||
final ValueChanged<int> onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Column(
|
||||
children: [_navigationBar(context), Expanded(child: navigationShell)],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Container _navigationBar(BuildContext context) {
|
||||
return Container(
|
||||
height: 64,
|
||||
decoration: BoxDecoration(
|
||||
border: Border(bottom: BorderSide(color: AppColors.grey30)),
|
||||
),
|
||||
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(width: 40),
|
||||
Image.asset(AssetsManager.logo, width: 59, height: 28),
|
||||
Spacer(),
|
||||
_bottomNavigationItem(
|
||||
context: context,
|
||||
text: 'Home',
|
||||
isActive: navigationShell.currentIndex == 0,
|
||||
onTap: () => onTap(0),
|
||||
iconPath: 'assets/icons/home.svg',
|
||||
activeIconPath: AssetsManager.homeActive,
|
||||
),
|
||||
SizedBox(width: 24),
|
||||
_bottomNavigationItem(
|
||||
context: context,
|
||||
text: 'Tenders',
|
||||
isActive: navigationShell.currentIndex == 1,
|
||||
onTap: () => onTap(1),
|
||||
iconPath: 'assets/icons/task_square.svg',
|
||||
activeIconPath: 'assets/icons/task-square_active.svg',
|
||||
),
|
||||
SizedBox(width: 24),
|
||||
_bottomNavigationItem(
|
||||
context: context,
|
||||
text: 'Profile',
|
||||
isActive: navigationShell.currentIndex == 2,
|
||||
onTap: () => onTap(2),
|
||||
iconPath: 'assets/icons/profile-circle.svg',
|
||||
activeIconPath: 'assets/icons/profile-circle_active.svg',
|
||||
),
|
||||
Spacer(),
|
||||
SizedBox(width: 59),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _bottomNavigationItem({
|
||||
required BuildContext context,
|
||||
required String text,
|
||||
required bool isActive,
|
||||
required VoidCallback onTap,
|
||||
required String iconPath,
|
||||
required String activeIconPath,
|
||||
}) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: SizedBox(
|
||||
width: 200.0,
|
||||
height: 64,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.asset(isActive ? activeIconPath : iconPath),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontSize: 12.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color:
|
||||
isActive
|
||||
? const Color(0xFF0164FF)
|
||||
: const Color(0xFF777777),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+13
-18
@@ -1,27 +1,22 @@
|
||||
// Shell screen with bottom navigation
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:tm_app/core/constants/assets.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
|
||||
import '../constants/assets.dart';
|
||||
import '../theme/colors.dart';
|
||||
import '../utils/size_config.dart';
|
||||
|
||||
class AppShellScreen extends StatelessWidget {
|
||||
const AppShellScreen({required this.navigationShell, super.key});
|
||||
class MobileShellPage extends StatelessWidget {
|
||||
const MobileShellPage({
|
||||
required this.navigationShell,
|
||||
required this.onTap,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final StatefulNavigationShell navigationShell;
|
||||
|
||||
void _gotoBranch(int index) {
|
||||
navigationShell.goBranch(
|
||||
index,
|
||||
initialLocation: index == navigationShell.currentIndex,
|
||||
);
|
||||
}
|
||||
final ValueChanged<int> onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
SizeConfig.init(context);
|
||||
return Scaffold(
|
||||
body: navigationShell,
|
||||
bottomNavigationBar: Container(
|
||||
@@ -37,7 +32,7 @@ class AppShellScreen extends StatelessWidget {
|
||||
context: context,
|
||||
text: 'Home',
|
||||
isActive: navigationShell.currentIndex == 0,
|
||||
onTap: () => _gotoBranch(0),
|
||||
onTap: () => onTap(0),
|
||||
iconPath: 'assets/icons/home.svg',
|
||||
activeIconPath: AssetsManager.homeActive,
|
||||
),
|
||||
@@ -45,7 +40,7 @@ class AppShellScreen extends StatelessWidget {
|
||||
context: context,
|
||||
text: 'Tenders',
|
||||
isActive: navigationShell.currentIndex == 1,
|
||||
onTap: () => _gotoBranch(1),
|
||||
onTap: () => onTap(1),
|
||||
iconPath: 'assets/icons/task_square.svg',
|
||||
activeIconPath: 'assets/icons/task-square_active.svg',
|
||||
),
|
||||
@@ -53,7 +48,7 @@ class AppShellScreen extends StatelessWidget {
|
||||
context: context,
|
||||
text: 'Profile',
|
||||
isActive: navigationShell.currentIndex == 2,
|
||||
onTap: () => _gotoBranch(2),
|
||||
onTap: () => onTap(2),
|
||||
iconPath: 'assets/icons/profile-circle.svg',
|
||||
activeIconPath: 'assets/icons/profile-circle_active.svg',
|
||||
),
|
||||
@@ -0,0 +1,126 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:tm_app/core/constants/assets.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
|
||||
class TabletShellPage extends StatelessWidget {
|
||||
TabletShellPage({
|
||||
required this.navigationShell,
|
||||
required this.onTap,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final StatefulNavigationShell navigationShell;
|
||||
final ValueChanged<int> onTap;
|
||||
|
||||
final GlobalKey<ScaffoldState> _key = GlobalKey();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
key: _key,
|
||||
body: Stack(
|
||||
children: [
|
||||
navigationShell,
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 64.0.h(),
|
||||
padding: EdgeInsets.symmetric(horizontal: 24.0.w()),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(color: AppColors.grey30, width: 1),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Image.asset(
|
||||
AssetsManager.logo,
|
||||
width: 59.0.w(),
|
||||
height: 28.0.h(),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () => _key.currentState!.openDrawer(),
|
||||
child: SvgPicture.asset(AssetsManager.menu),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
drawer: Container(
|
||||
width: 200.0.w(),
|
||||
height: double.infinity,
|
||||
color: AppColors.backgroundColor,
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 48.0.h()),
|
||||
_drawerItem(
|
||||
context: context,
|
||||
text: 'Home',
|
||||
isActive: navigationShell.currentIndex == 0,
|
||||
onTap: () => onTap(0),
|
||||
iconPath: AssetsManager.home,
|
||||
activeIconPath: AssetsManager.homeActive,
|
||||
),
|
||||
_drawerItem(
|
||||
context: context,
|
||||
text: 'Tenders',
|
||||
isActive: navigationShell.currentIndex == 1,
|
||||
onTap: () => onTap(1),
|
||||
iconPath: AssetsManager.tenders,
|
||||
activeIconPath: AssetsManager.tendersActive,
|
||||
),
|
||||
_drawerItem(
|
||||
context: context,
|
||||
text: 'Profile',
|
||||
isActive: navigationShell.currentIndex == 2,
|
||||
onTap: () => onTap(2),
|
||||
iconPath: AssetsManager.profile,
|
||||
activeIconPath: AssetsManager.profileActive,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _drawerItem({
|
||||
required BuildContext context,
|
||||
required String text,
|
||||
required bool isActive,
|
||||
required VoidCallback onTap,
|
||||
required String iconPath,
|
||||
required String activeIconPath,
|
||||
}) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: SizedBox(
|
||||
width: 200.0.w(),
|
||||
height: 64.0.h(),
|
||||
child: Padding(
|
||||
padding: EdgeInsetsDirectional.only(start: 24.0.w()),
|
||||
child: Row(
|
||||
children: [
|
||||
SvgPicture.asset(isActive ? activeIconPath : iconPath),
|
||||
SizedBox(width: 8.0.w()),
|
||||
Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontSize: 12.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color:
|
||||
isActive
|
||||
? const Color(0xFF0164FF)
|
||||
: const Color(0xFF777777),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'colors.dart';
|
||||
|
||||
class ThemeProvider extends ChangeNotifier {
|
||||
class ThemeProvider with ChangeNotifier {
|
||||
static const String _themeKey = 'theme_mode';
|
||||
static BuildContext? _context;
|
||||
|
||||
@@ -40,6 +40,7 @@ class ThemeProvider extends ChangeNotifier {
|
||||
|
||||
// Notify listeners after loading
|
||||
notifyListeners();
|
||||
_rebuildAllChildren();
|
||||
}
|
||||
|
||||
Future<void> toggleTheme() async {
|
||||
|
||||
@@ -10,7 +10,7 @@ class AuthService {
|
||||
// شبیهسازی API Call
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
|
||||
if (email == 'test@example.com' && password == 'password123') {
|
||||
if (email == 'a' && password == 'a') {
|
||||
return {'token': 'xyz123', 'name': 'John Doe'};
|
||||
} else {
|
||||
throw Exception('Invalid credentials');
|
||||
|
||||
@@ -43,6 +43,7 @@ class _MyAppState extends State<MyApp> {
|
||||
|
||||
return MaterialApp.router(
|
||||
title: 'Flutter Demo',
|
||||
debugShowCheckedModeBanner: false,
|
||||
scrollBehavior: MaterialScrollBehavior().copyWith(
|
||||
dragDevices: {
|
||||
PointerDeviceKind.mouse,
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tm_app/views/tenders/models/tender_model.dart';
|
||||
|
||||
class TendersViewModel with ChangeNotifier {
|
||||
List<Tender> likedTenders = [];
|
||||
List<Tender> dislikedTenders = [];
|
||||
int currentPage = 1;
|
||||
List<Tender> rejectedTenders = [];
|
||||
List<Tender> submittedTenders = [];
|
||||
|
||||
void onDislike(Tender tender) {
|
||||
if (likedTenders.contains(tender)) {
|
||||
likedTenders.remove(tender);
|
||||
if (submittedTenders.contains(tender)) {
|
||||
submittedTenders.remove(tender);
|
||||
}
|
||||
}
|
||||
|
||||
if (!dislikedTenders.contains(tender)) {
|
||||
dislikedTenders.add(tender);
|
||||
if (submittedTenders.contains(tender)) {
|
||||
submittedTenders.remove(tender);
|
||||
}
|
||||
} else {
|
||||
dislikedTenders.remove(tender);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void onLike(Tender tender) {
|
||||
if (dislikedTenders.contains(tender)) {
|
||||
dislikedTenders.remove(tender);
|
||||
if (rejectedTenders.contains(tender)) {
|
||||
rejectedTenders.remove(tender);
|
||||
}
|
||||
}
|
||||
|
||||
if (!likedTenders.contains(tender)) {
|
||||
likedTenders.add(tender);
|
||||
if (rejectedTenders.contains(tender)) {
|
||||
rejectedTenders.remove(tender);
|
||||
}
|
||||
} else {
|
||||
likedTenders.remove(tender);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void onReject(Tender tender) {
|
||||
if (rejectedTenders.contains(tender)) {
|
||||
rejectedTenders.remove(tender);
|
||||
} else if (submittedTenders.contains(tender)) {
|
||||
submittedTenders.remove(tender);
|
||||
rejectedTenders.add(tender);
|
||||
if (likedTenders.contains(tender)) {
|
||||
likedTenders.remove(tender);
|
||||
}
|
||||
} else {
|
||||
rejectedTenders.add(tender);
|
||||
if (likedTenders.contains(tender)) {
|
||||
likedTenders.remove(tender);
|
||||
}
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void onSubmit(Tender tender) {
|
||||
if (submittedTenders.contains(tender)) {
|
||||
submittedTenders.remove(tender);
|
||||
} else if (rejectedTenders.contains(tender)) {
|
||||
rejectedTenders.remove(tender);
|
||||
submittedTenders.add(tender);
|
||||
if (dislikedTenders.contains(tender)) {
|
||||
dislikedTenders.remove(tender);
|
||||
}
|
||||
} else {
|
||||
submittedTenders.add(tender);
|
||||
if (dislikedTenders.contains(tender)) {
|
||||
dislikedTenders.remove(tender);
|
||||
}
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
@@ -12,9 +12,9 @@ class TenderDetailScreen extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
SizeConfig.init(context);
|
||||
return ResponsiveBuilder(
|
||||
mobile: TenderDetailMobilePage(tenderId: 'JNDFKMDV-100JF',),
|
||||
tablet: TenderDetailTabletPage(tenderId: 'JNDFKMDV-100JF',),
|
||||
desktop: TenderDetailDesktopPage(tenderId: 'JNDFKMDV-100JF',),
|
||||
mobile: TenderDetailMobilePage(tenderId: 'JNDFKMDV-100JF'),
|
||||
tablet: TenderDetailTabletPage(tenderId: 'JNDFKMDV-100JF'),
|
||||
desktop: TenderDetailDesktopPage(tenderId: 'JNDFKMDV-100JF'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/data/services/model/home/home_response/home_response_model.dart';
|
||||
import 'package:tm_app/view_models/home_view_model.dart';
|
||||
import 'package:tm_app/views/home/widgets/notification_card.dart';
|
||||
|
||||
import '../widgets.dart';
|
||||
|
||||
@@ -32,6 +33,8 @@ class DesktopHomePage extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(height: 55),
|
||||
SizedBox(width: 780, child: NotificationCard()),
|
||||
SizedBox(height: 40.0),
|
||||
_progressBarsRow(homeViewModel.homeResponse!),
|
||||
SizedBox(height: 32.0.h()),
|
||||
_firstTenderCardsRow(context, homeViewModel.homeResponse!),
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/data/services/model/home/home_response/home_response_model.dart';
|
||||
import 'package:tm_app/view_models/home_view_model.dart';
|
||||
import 'package:tm_app/views/home/widgets/notification_card.dart';
|
||||
import 'package:tm_app/views/shared/tender_app_bar.dart';
|
||||
|
||||
import '../widgets.dart';
|
||||
@@ -63,6 +64,8 @@ class MobileHomePage extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 18.0.h()),
|
||||
NotificationCard(),
|
||||
SizedBox(height: 32.0.h()),
|
||||
_progressBarsRow(homeViewModel.homeResponse!),
|
||||
SizedBox(height: 32.0.h()),
|
||||
_firstTenderCardsRow(context, homeViewModel.homeResponse!),
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/data/services/model/home/home_response/home_response_model.dart';
|
||||
import 'package:tm_app/view_models/home_view_model.dart';
|
||||
import 'package:tm_app/views/home/widgets/notification_card.dart';
|
||||
|
||||
import '../widgets.dart';
|
||||
|
||||
@@ -38,6 +39,8 @@ class TabletHomePage extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
NotificationCard(),
|
||||
SizedBox(height: 40.0.h()),
|
||||
_progressBarsRow(homeViewModel.homeResponse!),
|
||||
SizedBox(height: 32.0.h()),
|
||||
_firstTenderCardsRow(context, homeViewModel.homeResponse!),
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:tm_app/core/constants/assets.dart';
|
||||
import 'package:tm_app/core/constants/strings.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
|
||||
class NotificationCard extends StatelessWidget {
|
||||
const NotificationCard({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
margin: EdgeInsets.symmetric(horizontal: 24.0.w()),
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.0.w(), vertical: 16.0.h()),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.mainBlue.withValues(alpha: 0.03),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
border: Border.all(color: AppColors.mainBlue.withValues(alpha: 0.15)),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SvgPicture.asset(AssetsManager.notification),
|
||||
SizedBox(width: 8.0.w()),
|
||||
Text(
|
||||
AppStrings.welcome,
|
||||
style: TextStyle(
|
||||
fontSize: 16.0.sp(),
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.grey80,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 8.0.h()),
|
||||
Text(
|
||||
'You’ve been invited Lorem ipsum dolor sit amet consectetur. Volutpat velit tincidunt amet diam. Placerat congue ut Sed facilisis',
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color: AppColors.grey60,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.0.h()),
|
||||
Padding(
|
||||
padding: EdgeInsetsDirectional.only(start: 28.0),
|
||||
child: Row(
|
||||
children: [
|
||||
SvgPicture.asset(AssetsManager.calendar),
|
||||
SizedBox(width: 4.0.w()),
|
||||
Text(
|
||||
'5 Min',
|
||||
style: TextStyle(
|
||||
fontSize: 14.0.sp(),
|
||||
color: AppColors.grey60,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,6 @@ class LoginMobilePage extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
SizeConfig.init(context);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
body: Center(
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../core/theme/colors.dart';
|
||||
import '../../../core/utils/size_config.dart';
|
||||
import '../widgets/theme_toggle.dart';
|
||||
import '../widgets/title_description.dart';
|
||||
|
||||
class DesktopProfilePage extends StatelessWidget {
|
||||
const DesktopProfilePage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
body: Center(
|
||||
child: SizedBox(
|
||||
width: 740,
|
||||
height: 507,
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 36.0.h()),
|
||||
TitleDescription(
|
||||
title: 'Company Name',
|
||||
description: 'Telecom Solutions GmbH',
|
||||
),
|
||||
TitleDescription(
|
||||
title: 'National ID',
|
||||
description: '12345678901',
|
||||
),
|
||||
TitleDescription(
|
||||
title: 'Registration No.',
|
||||
description: 'DE-55667788',
|
||||
),
|
||||
TitleDescription(
|
||||
title: 'Business Type',
|
||||
description: 'Telecommunications',
|
||||
),
|
||||
TitleDescription(title: 'Founded', description: '2010'),
|
||||
SizedBox(height: 24.0.h()),
|
||||
// Theme Toggle
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 16.0.w(),
|
||||
vertical: 12.0.h(),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.surfaceColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: AppColors.borderColor),
|
||||
),
|
||||
child: const ThemeToggle(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+7
-60
@@ -1,13 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tm_app/core/constants/strings.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/views/shared/tender_app_bar.dart';
|
||||
|
||||
import '../../core/theme/colors.dart';
|
||||
import '../../widgets/theme_toggle.dart';
|
||||
import '../../../core/constants/strings.dart';
|
||||
import '../../../core/theme/colors.dart';
|
||||
import '../../../core/utils/size_config.dart';
|
||||
import '../widgets/theme_toggle.dart';
|
||||
import '../widgets/title_description.dart';
|
||||
|
||||
class ProfileScreen extends StatelessWidget {
|
||||
const ProfileScreen({super.key});
|
||||
class MobileProfilePage extends StatelessWidget {
|
||||
const MobileProfilePage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -61,57 +62,3 @@ class ProfileScreen extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TitleDescription extends StatelessWidget {
|
||||
const TitleDescription({
|
||||
required this.title,
|
||||
required this.description,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final String description;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: 24.0.h()),
|
||||
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: 43.0.h(),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 16.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color: AppColors.grey60,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
description,
|
||||
style: TextStyle(
|
||||
fontSize: 16.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color: AppColors.grey80,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 1.0.h(),
|
||||
color: AppColors.dividerColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tm_app/views/shared/responsive_builder.dart';
|
||||
|
||||
import '../pages/d_profile_page.dart';
|
||||
import '../pages/m_profile_page.dart';
|
||||
import '../pages/t_profile_page.dart';
|
||||
|
||||
class ProfileScreen extends StatelessWidget {
|
||||
const ProfileScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(
|
||||
mobile: MobileProfilePage(),
|
||||
tablet: TabletProfilePage(),
|
||||
desktop: DesktopProfilePage(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../core/theme/colors.dart';
|
||||
import '../../../core/utils/size_config.dart';
|
||||
import '../widgets/theme_toggle.dart';
|
||||
import '../widgets/title_description.dart';
|
||||
|
||||
class TabletProfilePage extends StatelessWidget {
|
||||
const TabletProfilePage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
body: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 24.0.w(),
|
||||
vertical: 16.0.h(),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 128.0.h()),
|
||||
TitleDescription(
|
||||
title: 'Company Name',
|
||||
description: 'Telecom Solutions GmbH',
|
||||
),
|
||||
TitleDescription(
|
||||
title: 'National ID',
|
||||
description: '12345678901',
|
||||
),
|
||||
TitleDescription(
|
||||
title: 'Registration No.',
|
||||
description: 'DE-55667788',
|
||||
),
|
||||
TitleDescription(
|
||||
title: 'Business Type',
|
||||
description: 'Telecommunications',
|
||||
),
|
||||
TitleDescription(title: 'Founded', description: '2010'),
|
||||
SizedBox(height: 24.0.h()),
|
||||
// Theme Toggle
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 16.0.w(),
|
||||
vertical: 12.0.h(),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.surfaceColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: AppColors.borderColor),
|
||||
),
|
||||
child: const ThemeToggle(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../core/theme/colors.dart';
|
||||
import '../core/theme/theme_provider.dart';
|
||||
import '../../../core/theme/colors.dart';
|
||||
import '../../../core/theme/theme_provider.dart';
|
||||
|
||||
class ThemeToggle extends StatelessWidget {
|
||||
const ThemeToggle({super.key});
|
||||
@@ -0,0 +1,57 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
|
||||
class TitleDescription extends StatelessWidget {
|
||||
const TitleDescription({
|
||||
required this.title,
|
||||
required this.description,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final String description;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: 24.0.h()),
|
||||
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: 43.0.h(),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 16.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color: AppColors.grey60,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
description,
|
||||
style: TextStyle(
|
||||
fontSize: 16.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color: AppColors.grey80,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 1.0.h(),
|
||||
color: AppColors.dividerColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,72 +1,103 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:tm_app/core/constants/assets.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
|
||||
PreferredSize tenderMobileAppBar({required String title}) {
|
||||
// PreferredSize tenderMobileAppBar({required String title}) {
|
||||
// return PreferredSize(
|
||||
// preferredSize: Size.fromHeight(kToolbarHeight.h()),
|
||||
// child: Container(
|
||||
// color: AppColors.backgroundColor,
|
||||
// padding: EdgeInsets.symmetric(horizontal: 24.0.w()),
|
||||
// height: 56.0.h(),
|
||||
// width: double.infinity,
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// children: [
|
||||
// Text(
|
||||
// title,
|
||||
// style: TextStyle(
|
||||
// fontSize: 20,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: AppColors.grey70,
|
||||
// ),
|
||||
// ),
|
||||
// Image.asset(
|
||||
// 'assets/icons/tenderLogo.png',
|
||||
// width: 59.0.w(),
|
||||
// height: 28.0.h(),
|
||||
// fit: BoxFit.cover,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
PreferredSizeWidget tenderMobileAppBar({required String title}) {
|
||||
return PreferredSize(
|
||||
preferredSize: Size.fromHeight(kToolbarHeight.h()),
|
||||
child: Container(
|
||||
color: AppColors.backgroundColor,
|
||||
padding: EdgeInsets.symmetric(horizontal: 24.0.w()),
|
||||
height: 56.0.h(),
|
||||
width: double.infinity,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColors.grey70,
|
||||
),
|
||||
preferredSize: const Size.fromHeight(56),
|
||||
child: AppBar(
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
elevation: 0,
|
||||
titleSpacing: 0,
|
||||
automaticallyImplyLeading: false,
|
||||
actions: [
|
||||
Image.asset(
|
||||
AssetsManager.tenderLogo,
|
||||
width: 59.0.w(),
|
||||
height: 28.0.h(),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
SizedBox(width: 24.0.w()),
|
||||
],
|
||||
title: Padding(
|
||||
padding: EdgeInsetsDirectional.only(start: 24.0.w()),
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
color: AppColors.grey70,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 20.0.sp(),
|
||||
),
|
||||
Image.asset(
|
||||
'assets/icons/tenderLogo.png',
|
||||
width: 59.0.w(),
|
||||
height: 28.0.h(),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
PreferredSize appBar({required String title, required BuildContext context}) {
|
||||
return PreferredSize(
|
||||
preferredSize: Size.fromHeight(kToolbarHeight.h()),
|
||||
child: Container(
|
||||
color: AppColors.backgroundColor,
|
||||
padding: EdgeInsets.symmetric(horizontal: 24.0.w()),
|
||||
height: 56.0.h(),
|
||||
width: double.infinity,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
AssetsManager.arrowLeft,
|
||||
width: 24.0.w(),
|
||||
height: 24.0.h(),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 2.0.w()),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColors.grey70,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
// PreferredSize appBar({required String title, required BuildContext context}) {
|
||||
// return PreferredSize(
|
||||
// preferredSize: Size.fromHeight(kToolbarHeight.h()),
|
||||
// child: Container(
|
||||
// color: AppColors.backgroundColor,
|
||||
// padding: EdgeInsets.symmetric(horizontal: 24.0.w()),
|
||||
// height: 56.0.h(),
|
||||
// width: double.infinity,
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// children: [
|
||||
// GestureDetector(
|
||||
// onTap: () {
|
||||
// Navigator.pop(context);
|
||||
// },
|
||||
// child: SvgPicture.asset(
|
||||
// AssetsManager.arrowLeft,
|
||||
// width: 24.0.w(),
|
||||
// height: 24.0.h(),
|
||||
// ),
|
||||
// ),
|
||||
// SizedBox(width: 2.0.w()),
|
||||
// Text(
|
||||
// title,
|
||||
// style: TextStyle(
|
||||
// fontSize: 20,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: AppColors.grey70,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/views/tenders/widgets/main_tenders_slider.dart';
|
||||
|
||||
class DesktopTendersPage extends StatelessWidget {
|
||||
const DesktopTendersPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
body: Center(
|
||||
child: SingleChildScrollView(
|
||||
child: SizedBox(
|
||||
width: 740,
|
||||
height: 800,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 24.0.h()),
|
||||
child: MainTendersSlider(isDesktop: true),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tm_app/core/constants/strings.dart';
|
||||
import 'package:tm_app/views/shared/tender_app_bar.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/views/tenders/widgets/main_tenders_slider.dart';
|
||||
|
||||
import '../../core/theme/colors.dart';
|
||||
import '../../core/utils/size_config.dart';
|
||||
import 'widgets/main_tenders_slider.dart';
|
||||
import '../../../core/constants/strings.dart';
|
||||
import '../../../core/theme/colors.dart';
|
||||
import '../../shared/tender_app_bar.dart';
|
||||
|
||||
class TendersScreen extends StatelessWidget {
|
||||
const TendersScreen({super.key});
|
||||
class MobileTendersPage extends StatelessWidget {
|
||||
const MobileTendersPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -0,0 +1,24 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/views/tenders/widgets/main_tenders_slider.dart';
|
||||
|
||||
class TabletTendersPage extends StatelessWidget {
|
||||
const TabletTendersPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 24.0.h()),
|
||||
child: MainTendersSlider(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tm_app/views/shared/responsive_builder.dart';
|
||||
|
||||
import 'd_tenders_page.dart';
|
||||
import 'm_tenders_page.dart';
|
||||
import 't_tenders_page.dart';
|
||||
|
||||
class TendersScreen extends StatelessWidget {
|
||||
const TendersScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(
|
||||
mobile: MobileTendersPage(),
|
||||
tablet: TabletTendersPage(),
|
||||
desktop: DesktopTendersPage(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:tm_app/core/routes/app_routes.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
|
||||
import '../../../core/constants/assets.dart';
|
||||
@@ -9,7 +8,8 @@ import '../models/tender_model.dart';
|
||||
import 'tender_card.dart';
|
||||
|
||||
class MainTendersSlider extends StatefulWidget {
|
||||
const MainTendersSlider({super.key});
|
||||
const MainTendersSlider({super.key, this.isDesktop = false});
|
||||
final bool isDesktop;
|
||||
|
||||
@override
|
||||
State<MainTendersSlider> createState() => _MainTendersSliderState();
|
||||
@@ -18,37 +18,7 @@ class MainTendersSlider extends StatefulWidget {
|
||||
class _MainTendersSliderState extends State<MainTendersSlider> {
|
||||
final PageController pageController = PageController(initialPage: 0);
|
||||
|
||||
List<Tender> likedTenders = [];
|
||||
List<Tender> dislikedTenders = [];
|
||||
int currentPage = 1;
|
||||
List<Tender> rejectedTenders = [];
|
||||
List<Tender> submittedTenders = [];
|
||||
|
||||
void _onDislike(Tender tender) {
|
||||
if (likedTenders.contains(tender)) {
|
||||
likedTenders.remove(tender);
|
||||
}
|
||||
|
||||
if (!dislikedTenders.contains(tender)) {
|
||||
dislikedTenders.add(tender);
|
||||
} else {
|
||||
dislikedTenders.remove(tender);
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void _onLike(Tender tender) {
|
||||
if (dislikedTenders.contains(tender)) {
|
||||
dislikedTenders.remove(tender);
|
||||
}
|
||||
|
||||
if (!likedTenders.contains(tender)) {
|
||||
likedTenders.add(tender);
|
||||
} else {
|
||||
likedTenders.remove(tender);
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void _goToPreviousPage() {
|
||||
pageController.previousPage(
|
||||
@@ -76,25 +46,6 @@ class _MainTendersSliderState extends State<MainTendersSlider> {
|
||||
});
|
||||
}
|
||||
|
||||
void _onReject(Tender tender) {
|
||||
if (rejectedTenders.contains(tender)) {
|
||||
rejectedTenders.remove(tender);
|
||||
} else {
|
||||
rejectedTenders.add(tender);
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void _onSubmit(Tender tender) {
|
||||
if (submittedTenders.contains(tender)) {
|
||||
submittedTenders.remove(tender);
|
||||
} else {
|
||||
submittedTenders.add(tender);
|
||||
}
|
||||
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
pageController.dispose();
|
||||
@@ -119,25 +70,7 @@ class _MainTendersSliderState extends State<MainTendersSlider> {
|
||||
},
|
||||
itemBuilder: (context, index) {
|
||||
final tender = sampleTenders[index];
|
||||
return TenderCard(
|
||||
date: tender.date,
|
||||
deadline: tender.deadline,
|
||||
title: tender.title,
|
||||
description: tender.description,
|
||||
tenderId: tender.tenderId,
|
||||
location: tender.location,
|
||||
country: tender.country,
|
||||
matchPercentage: tender.matchPercentage,
|
||||
isLiked: likedTenders.contains(tender),
|
||||
isDisliked: dislikedTenders.contains(tender),
|
||||
onDislike: () => _onDislike(tender),
|
||||
onLike: () => _onLike(tender),
|
||||
onApply: () => TenderDetailRouteData().push(context),
|
||||
onReject: () => _onReject(tender),
|
||||
onSubmit: () => _onSubmit(tender),
|
||||
rejected: rejectedTenders.contains(tender),
|
||||
submitted: submittedTenders.contains(tender),
|
||||
);
|
||||
return TenderCard(tender: tender, isDesktop: widget.isDesktop);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,167 +1,207 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tm_app/view_models/tenders_view_model.dart';
|
||||
import 'package:tm_app/views/tenders/models/tender_model.dart';
|
||||
|
||||
import '../../../core/constants/assets.dart';
|
||||
import '../../../core/theme/colors.dart';
|
||||
import '../../../core/utils/size_config.dart';
|
||||
|
||||
class TenderActionButtons extends StatelessWidget {
|
||||
final VoidCallback? onLike;
|
||||
final VoidCallback? onDislike;
|
||||
final VoidCallback? onClose;
|
||||
final VoidCallback? onReject;
|
||||
final VoidCallback? onSubmit;
|
||||
final bool isLiked;
|
||||
final bool isDisliked;
|
||||
final bool rejected;
|
||||
final bool submitted;
|
||||
final Tender tender;
|
||||
final bool isDesktop;
|
||||
|
||||
const TenderActionButtons({
|
||||
required this.tender,
|
||||
this.isDesktop = false,
|
||||
super.key,
|
||||
this.onLike,
|
||||
this.onDislike,
|
||||
this.onClose,
|
||||
this.onReject,
|
||||
this.onSubmit,
|
||||
this.isLiked = false,
|
||||
this.isDisliked = false,
|
||||
this.rejected = false,
|
||||
this.submitted = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final viewModel = context.watch<TendersViewModel>();
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(width: 16.0.w()),
|
||||
|
||||
// Dislike button
|
||||
GestureDetector(
|
||||
onTap: onDislike,
|
||||
child: Column(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
width: 32.0.w(),
|
||||
height: 32.0.w(),
|
||||
fit: BoxFit.cover,
|
||||
AssetsManager.dislike,
|
||||
colorFilter: ColorFilter.mode(
|
||||
isDisliked ? AppColors.errorColor : AppColors.grey50,
|
||||
BlendMode.srcATop,
|
||||
Tooltip(
|
||||
message: 'Dislike',
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
viewModel.onDislike(tender);
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
width: 32.0.w(),
|
||||
height: 32.0.w(),
|
||||
fit: BoxFit.cover,
|
||||
AssetsManager.dislike,
|
||||
colorFilter: ColorFilter.mode(
|
||||
viewModel.dislikedTenders.contains(tender)
|
||||
? AppColors.errorColor
|
||||
: AppColors.grey50,
|
||||
BlendMode.srcATop,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.0.h()),
|
||||
Text(
|
||||
'Like',
|
||||
style: TextStyle(
|
||||
fontSize: 12.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color: isDisliked ? AppColors.errorColor : AppColors.grey50,
|
||||
),
|
||||
),
|
||||
],
|
||||
SizedBox(height: 8.0.h()),
|
||||
if (!isDesktop)
|
||||
Text(
|
||||
'Like',
|
||||
style: TextStyle(
|
||||
fontSize: 12.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color:
|
||||
viewModel.dislikedTenders.contains(tender)
|
||||
? AppColors.errorColor
|
||||
: AppColors.grey50,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 32.0.w()),
|
||||
//reject button
|
||||
GestureDetector(
|
||||
onTap: onReject,
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: 32.0.w(),
|
||||
height: 32.0.w(),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: rejected ? AppColors.errorColor : AppColors.grey60,
|
||||
width: 1.5.w(),
|
||||
Tooltip(
|
||||
message: 'Reject',
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
viewModel.onReject(tender);
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: 32.0.w(),
|
||||
height: 32.0.w(),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color:
|
||||
viewModel.rejectedTenders.contains(tender)
|
||||
? AppColors.errorColor
|
||||
: AppColors.grey60,
|
||||
width: 1.5.w(),
|
||||
),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Icon(
|
||||
Icons.close,
|
||||
color:
|
||||
viewModel.rejectedTenders.contains(tender)
|
||||
? AppColors.errorColor
|
||||
: AppColors.grey60,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Icon(
|
||||
Icons.close,
|
||||
color: rejected ? AppColors.errorColor : AppColors.grey60,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.0.h()),
|
||||
Text(
|
||||
'Reject',
|
||||
style: TextStyle(
|
||||
fontSize: 12.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color: rejected ? AppColors.errorColor : AppColors.grey60,
|
||||
),
|
||||
),
|
||||
],
|
||||
SizedBox(height: 8.0.h()),
|
||||
if (!isDesktop)
|
||||
Text(
|
||||
'Reject',
|
||||
style: TextStyle(
|
||||
fontSize: 12.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color:
|
||||
viewModel.rejectedTenders.contains(tender)
|
||||
? AppColors.errorColor
|
||||
: AppColors.grey60,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 32.0.w()),
|
||||
//submit button
|
||||
GestureDetector(
|
||||
onTap: onSubmit,
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: 32.0.w(),
|
||||
height: 32.0.w(),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color:
|
||||
submitted ? AppColors.successColor : AppColors.grey60,
|
||||
width: 1.5.w(),
|
||||
Tooltip(
|
||||
message: 'Submit',
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
viewModel.onSubmit(tender);
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: 32.0.w(),
|
||||
height: 32.0.w(),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color:
|
||||
viewModel.submittedTenders.contains(tender)
|
||||
? AppColors.successColor
|
||||
: AppColors.grey60,
|
||||
width: 1.5.w(),
|
||||
),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: SvgPicture.asset(
|
||||
AssetsManager.tick,
|
||||
colorFilter: ColorFilter.mode(
|
||||
viewModel.submittedTenders.contains(tender)
|
||||
? AppColors.successColor
|
||||
: AppColors.grey60,
|
||||
BlendMode.srcATop,
|
||||
),
|
||||
),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: SvgPicture.asset(
|
||||
AssetsManager.tick,
|
||||
colorFilter: ColorFilter.mode(
|
||||
submitted ? AppColors.successColor : AppColors.grey60,
|
||||
BlendMode.srcATop,
|
||||
SizedBox(height: 8.0.h()),
|
||||
if (!isDesktop)
|
||||
Text(
|
||||
'Submit',
|
||||
style: TextStyle(
|
||||
fontSize: 12.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color:
|
||||
viewModel.submittedTenders.contains(tender)
|
||||
? AppColors.successColor
|
||||
: AppColors.grey60,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.0.h()),
|
||||
Text(
|
||||
'Submit',
|
||||
style: TextStyle(
|
||||
fontSize: 12.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color: submitted ? AppColors.successColor : AppColors.grey60,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 32.0.w()),
|
||||
// Like button
|
||||
GestureDetector(
|
||||
onTap: onLike,
|
||||
child: Column(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
width: 32.0.w(),
|
||||
height: 32.0.w(),
|
||||
fit: BoxFit.cover,
|
||||
AssetsManager.like,
|
||||
colorFilter: ColorFilter.mode(
|
||||
isLiked ? AppColors.successColor : AppColors.grey50,
|
||||
BlendMode.srcATop,
|
||||
Tooltip(
|
||||
message: 'Like',
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
viewModel.onLike(tender);
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
width: 32.0.w(),
|
||||
height: 32.0.w(),
|
||||
fit: BoxFit.cover,
|
||||
AssetsManager.like,
|
||||
colorFilter: ColorFilter.mode(
|
||||
viewModel.likedTenders.contains(tender)
|
||||
? AppColors.successColor
|
||||
: AppColors.grey50,
|
||||
BlendMode.srcATop,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.0.h()),
|
||||
Text(
|
||||
'Like',
|
||||
style: TextStyle(
|
||||
fontSize: 12.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color: isLiked ? AppColors.successColor : AppColors.grey50,
|
||||
),
|
||||
),
|
||||
],
|
||||
SizedBox(height: 8.0.h()),
|
||||
if (!isDesktop)
|
||||
Text(
|
||||
'Like',
|
||||
style: TextStyle(
|
||||
fontSize: 12.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color:
|
||||
viewModel.likedTenders.contains(tender)
|
||||
? AppColors.successColor
|
||||
: AppColors.grey50,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -1,52 +1,18 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:tm_app/core/constants/strings.dart';
|
||||
import 'package:tm_app/core/routes/app_routes.dart';
|
||||
|
||||
import '../../../core/constants/assets.dart';
|
||||
import '../../../core/theme/colors.dart';
|
||||
import '../../../core/utils/size_config.dart';
|
||||
import '../models/tender_model.dart';
|
||||
import 'tender_action_buttons.dart';
|
||||
|
||||
class TenderCard extends StatelessWidget {
|
||||
final String date;
|
||||
final String deadline;
|
||||
final String title;
|
||||
final String description;
|
||||
final String tenderId;
|
||||
final String location;
|
||||
final String country;
|
||||
final double matchPercentage;
|
||||
final VoidCallback? onApply;
|
||||
final VoidCallback? onLike;
|
||||
final VoidCallback? onDislike;
|
||||
final VoidCallback? onClose;
|
||||
final bool isLiked;
|
||||
final bool isDisliked;
|
||||
final VoidCallback? onReject;
|
||||
final VoidCallback? onSubmit;
|
||||
final bool rejected;
|
||||
final bool submitted;
|
||||
const TenderCard({
|
||||
required this.date,
|
||||
required this.deadline,
|
||||
required this.title,
|
||||
required this.description,
|
||||
required this.tenderId,
|
||||
required this.location,
|
||||
required this.country,
|
||||
required this.matchPercentage,
|
||||
this.onApply,
|
||||
this.onLike,
|
||||
this.onDislike,
|
||||
this.onClose,
|
||||
this.isLiked = false,
|
||||
this.isDisliked = false,
|
||||
this.onReject,
|
||||
this.onSubmit,
|
||||
this.rejected = false,
|
||||
this.submitted = false,
|
||||
super.key,
|
||||
});
|
||||
final Tender tender;
|
||||
final bool isDesktop;
|
||||
const TenderCard({required this.tender, super.key, this.isDesktop = false});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -71,13 +37,23 @@ class TenderCard extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Date
|
||||
Text(
|
||||
date,
|
||||
style: TextStyle(
|
||||
color: AppColors.grey60,
|
||||
fontSize: 12.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
tender.date,
|
||||
style: TextStyle(
|
||||
color: AppColors.grey60,
|
||||
fontSize: 12.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
if (isDesktop)
|
||||
TenderActionButtons(
|
||||
tender: tender,
|
||||
isDesktop: isDesktop,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 12.0.h()),
|
||||
|
||||
@@ -102,7 +78,7 @@ class TenderCard extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
deadline,
|
||||
tender.deadline,
|
||||
style: TextStyle(
|
||||
color: AppColors.grey80,
|
||||
fontSize: 14.0.sp(),
|
||||
@@ -116,7 +92,7 @@ class TenderCard extends StatelessWidget {
|
||||
|
||||
// Title
|
||||
Text(
|
||||
title,
|
||||
tender.title,
|
||||
style: TextStyle(
|
||||
color: AppColors.grey80,
|
||||
fontSize: 16.0.sp(),
|
||||
@@ -127,7 +103,7 @@ class TenderCard extends StatelessWidget {
|
||||
|
||||
// Description
|
||||
Text(
|
||||
description,
|
||||
tender.description,
|
||||
style: TextStyle(
|
||||
color: AppColors.grey70,
|
||||
fontSize: 16.0.sp(),
|
||||
@@ -139,7 +115,7 @@ class TenderCard extends StatelessWidget {
|
||||
|
||||
// Tender ID
|
||||
Text(
|
||||
tenderId,
|
||||
tender.tenderId,
|
||||
style: TextStyle(
|
||||
color: AppColors.grey,
|
||||
fontSize: 14.0.sp(),
|
||||
@@ -168,7 +144,7 @@ class TenderCard extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${matchPercentage.toInt()}%',
|
||||
'${tender.matchPercentage.toInt()}%',
|
||||
style: TextStyle(
|
||||
color: AppColors.secondaryTextColor,
|
||||
fontSize: 16.0.sp(),
|
||||
@@ -189,7 +165,7 @@ class TenderCard extends StatelessWidget {
|
||||
),
|
||||
child: FractionallySizedBox(
|
||||
alignment: Alignment.centerLeft,
|
||||
widthFactor: matchPercentage / 100,
|
||||
widthFactor: tender.matchPercentage / 100,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.secondary50,
|
||||
@@ -210,7 +186,7 @@ class TenderCard extends StatelessWidget {
|
||||
SvgPicture.asset(AssetsManager.location),
|
||||
SizedBox(width: 4.0.w()),
|
||||
Text(
|
||||
location,
|
||||
tender.location,
|
||||
style: TextStyle(
|
||||
color: AppColors.grey80,
|
||||
fontSize: 14.0.sp(),
|
||||
@@ -230,7 +206,9 @@ class TenderCard extends StatelessWidget {
|
||||
|
||||
// See More button
|
||||
GestureDetector(
|
||||
onTap: onApply,
|
||||
onTap: () {
|
||||
TenderDetailRouteData().push(context);
|
||||
},
|
||||
child: Container(
|
||||
width: 108.0.w(),
|
||||
height: 40.0.h(),
|
||||
@@ -258,17 +236,7 @@ class TenderCard extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
SizedBox(height: 72.0.h()),
|
||||
TenderActionButtons(
|
||||
isDisliked: isDisliked,
|
||||
isLiked: isLiked,
|
||||
onDislike: onDislike,
|
||||
onLike: onLike,
|
||||
onClose: onClose,
|
||||
onReject: onReject,
|
||||
onSubmit: onSubmit,
|
||||
rejected: rejected,
|
||||
submitted: submitted,
|
||||
),
|
||||
if (!isDesktop) TenderActionButtons(tender: tender),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user