Merge pull request 'check and fix in ui' (#26) from all_fixes into main
Reviewed-on: https://repo.ravanertebat.com/TM/tm_app/pulls/26
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.33203 1.33594V3.33594" stroke="#777777" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.668 1.33594V3.33594" stroke="#777777" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M2.33203 6.0625H13.6654" stroke="#777777" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M14 5.66927V11.3359C14 13.3359 13 14.6693 10.6667 14.6693H5.33333C3 14.6693 2 13.3359 2 11.3359V5.66927C2 3.66927 3 2.33594 5.33333 2.33594H10.6667C13 2.33594 14 3.66927 14 5.66927Z" stroke="#777777" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7.99764 9.13021H8.00363" stroke="#777777" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M5.52889 9.13021H5.53488" stroke="#777777" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M5.52889 11.1302H5.53488" stroke="#777777" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 9.33594H28" stroke="#888888" stroke-width="1.5" stroke-linecap="round"/>
|
||||
<path d="M4 16H28" stroke="#888888" stroke-width="1.5" stroke-linecap="round"/>
|
||||
<path d="M4 22.6641H28" stroke="#888888" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 353 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 5.36719V8.14219" stroke="#0164FF" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round"/>
|
||||
<path d="M10.0171 1.66406C6.9504 1.66406 4.46707 4.1474 4.46707 7.21406V8.96406C4.46707 9.53073 4.23374 10.3807 3.94207 10.8641L2.88374 12.6307C2.23374 13.7224 2.68374 14.9391 3.88374 15.3391C7.86707 16.6641 12.1754 16.6641 16.1587 15.3391C17.2837 14.9641 17.7671 13.6474 17.1587 12.6307L16.1004 10.8641C15.8087 10.3807 15.5754 9.5224 15.5754 8.96406V7.21406C15.5671 4.16406 13.0671 1.66406 10.0171 1.66406Z" stroke="#0164FF" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round"/>
|
||||
<path d="M12.7766 15.6797C12.7766 17.2047 11.5266 18.4547 10.0016 18.4547C9.24323 18.4547 8.54323 18.138 8.04323 17.638C7.54323 17.138 7.22656 16.438 7.22656 15.6797" stroke="#0164FF" stroke-width="1.5" stroke-miterlimit="10"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 938 B |
@@ -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(
|
||||
'assets/icons/tenderLogo.png',
|
||||
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(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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,44 +1,38 @@
|
||||
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,
|
||||
Tooltip(
|
||||
message: 'Dislike',
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
viewModel.onDislike(tender);
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
@@ -47,61 +41,37 @@ class TenderActionButtons extends StatelessWidget {
|
||||
fit: BoxFit.cover,
|
||||
AssetsManager.dislike,
|
||||
colorFilter: ColorFilter.mode(
|
||||
isDisliked ? AppColors.errorColor : AppColors.grey50,
|
||||
viewModel.dislikedTenders.contains(tender)
|
||||
? AppColors.errorColor
|
||||
: AppColors.grey50,
|
||||
BlendMode.srcATop,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.0.h()),
|
||||
if (!isDesktop)
|
||||
Text(
|
||||
'Like',
|
||||
style: TextStyle(
|
||||
fontSize: 12.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color: isDisliked ? AppColors.errorColor : AppColors.grey50,
|
||||
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(),
|
||||
),
|
||||
),
|
||||
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(width: 32.0.w()),
|
||||
//submit button
|
||||
GestureDetector(
|
||||
onTap: onSubmit,
|
||||
Tooltip(
|
||||
message: 'Reject',
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
viewModel.onReject(tender);
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
@@ -111,7 +81,59 @@ class TenderActionButtons extends StatelessWidget {
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color:
|
||||
submitted ? AppColors.successColor : AppColors.grey60,
|
||||
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,
|
||||
),
|
||||
),
|
||||
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
|
||||
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(),
|
||||
),
|
||||
),
|
||||
@@ -119,27 +141,38 @@ class TenderActionButtons extends StatelessWidget {
|
||||
child: SvgPicture.asset(
|
||||
AssetsManager.tick,
|
||||
colorFilter: ColorFilter.mode(
|
||||
submitted ? AppColors.successColor : AppColors.grey60,
|
||||
viewModel.submittedTenders.contains(tender)
|
||||
? 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: submitted ? AppColors.successColor : AppColors.grey60,
|
||||
color:
|
||||
viewModel.submittedTenders.contains(tender)
|
||||
? AppColors.successColor
|
||||
: AppColors.grey60,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 32.0.w()),
|
||||
// Like button
|
||||
GestureDetector(
|
||||
onTap: onLike,
|
||||
Tooltip(
|
||||
message: 'Like',
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
viewModel.onLike(tender);
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
@@ -148,22 +181,29 @@ class TenderActionButtons extends StatelessWidget {
|
||||
fit: BoxFit.cover,
|
||||
AssetsManager.like,
|
||||
colorFilter: ColorFilter.mode(
|
||||
isLiked ? AppColors.successColor : AppColors.grey50,
|
||||
viewModel.likedTenders.contains(tender)
|
||||
? AppColors.successColor
|
||||
: AppColors.grey50,
|
||||
BlendMode.srcATop,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.0.h()),
|
||||
if (!isDesktop)
|
||||
Text(
|
||||
'Like',
|
||||
style: TextStyle(
|
||||
fontSize: 12.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color: isLiked ? AppColors.successColor : AppColors.grey50,
|
||||
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,14 +37,24 @@ class TenderCard extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Date
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
date,
|
||||
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()),
|
||||
|
||||
// Deadline badge
|
||||
@@ -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