You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
409 lines
14 KiB
409 lines
14 KiB
1 day ago
|
import 'package:flutter/material.dart';
|
||
|
import 'package:supplier_new/common/settings.dart';
|
||
|
void main() => runApp(const MaterialApp(home: ResourcesScreen()));
|
||
|
|
||
|
|
||
|
class ResourcesScreen extends StatefulWidget {
|
||
|
const ResourcesScreen({super.key});
|
||
|
@override
|
||
|
State<ResourcesScreen> createState() => _ResourcesScreenState();
|
||
|
}
|
||
|
|
||
|
class _ResourcesScreenState extends State<ResourcesScreen> {
|
||
|
int selectedTab = 0;
|
||
|
String search = '';
|
||
|
|
||
|
final List<Map<String, dynamic>> items = [
|
||
|
{
|
||
|
'title': 'Tanker Name',
|
||
|
'subtitle': 'Drinking water - 10,000 L',
|
||
|
'status': ['filled', 'available'],
|
||
|
'code': 'TS 07 J 3492',
|
||
|
'owner': 'Ramesh Krishna'
|
||
|
},
|
||
|
{
|
||
|
'title': 'Drinking Water - 15,000L',
|
||
|
'subtitle': 'Drinking water - 15,000 L',
|
||
|
'status': ['empty', 'available'],
|
||
|
'code': 'TS 07 J 3492',
|
||
|
'owner': 'Ramesh Krishna'
|
||
|
},
|
||
|
{
|
||
|
'title': 'Tanker Name',
|
||
|
'subtitle': 'Drinking water - 10,000 L',
|
||
|
'status': ['filled', 'in-use'],
|
||
|
'code': 'TS 07 J 3492',
|
||
|
'owner': 'Ramesh Krishna'
|
||
|
},
|
||
|
{
|
||
|
'title': 'Drinking Water - 15,000L',
|
||
|
'subtitle': 'Drinking water - 15,000 L',
|
||
|
'status': ['empty', 'in-use'],
|
||
|
'code': 'TS 07 J 3492',
|
||
|
'owner': 'Ramesh Krishna'
|
||
|
},
|
||
|
{
|
||
|
'title': 'Tanker Name',
|
||
|
'subtitle': 'Drinking water - 10,000 L',
|
||
|
'status': ['filled', 'maintenance'],
|
||
|
'code': 'TS 07 J 3492',
|
||
|
'owner': 'Ramesh Krishna'
|
||
|
},
|
||
|
];
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
final filtered = items.where((it) {
|
||
|
final q = search.trim().toLowerCase();
|
||
|
if (q.isEmpty) return true;
|
||
|
return it['title'].toLowerCase().contains(q) ||
|
||
|
it['subtitle'].toLowerCase().contains(q) ||
|
||
|
it['owner'].toLowerCase().contains(q);
|
||
|
}).toList();
|
||
|
|
||
|
return Scaffold(
|
||
|
backgroundColor: const Color(0xFFF6F6F7),
|
||
|
appBar: AppBar(
|
||
|
backgroundColor: Colors.white,
|
||
|
elevation: 0.7,
|
||
|
leading: IconButton(
|
||
|
icon: const Icon(Icons.arrow_back, color: Colors.black87),
|
||
|
onPressed: () => Navigator.of(context).maybePop(),
|
||
|
),
|
||
|
title: Text(
|
||
|
'Resources',
|
||
|
style: fontTextStyle(16, const Color(0xFF2A2A2A), FontWeight.w600),
|
||
|
),
|
||
|
centerTitle: false,
|
||
|
actions: [
|
||
|
TextButton(
|
||
|
onPressed: () {},
|
||
|
child: Text('HELP', style: fontTextStyle(12, const Color(0xFF8270DB), FontWeight.w600)),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
|
||
|
body: SafeArea(
|
||
|
child: Padding(
|
||
|
padding: const EdgeInsets.fromLTRB(16, 12, 16, 0),
|
||
|
child: Column(
|
||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
children: [
|
||
|
// Segmented tabs
|
||
|
Container(
|
||
|
padding: const EdgeInsets.all(6),
|
||
|
decoration: BoxDecoration(
|
||
|
color: const Color(0xFFF1F1F3),
|
||
|
borderRadius: BorderRadius.circular(24),
|
||
|
),
|
||
|
child: Row(
|
||
|
mainAxisSize: MainAxisSize.min,
|
||
|
children: List.generate(3, (i) {
|
||
|
final labels = ['Fleet', 'Drivers', 'Sources'];
|
||
|
final isSelected = selectedTab == i;
|
||
|
return GestureDetector(
|
||
|
onTap: () => setState(() => selectedTab = i),
|
||
|
child: Container(
|
||
|
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
|
||
|
margin: const EdgeInsets.symmetric(horizontal: 4),
|
||
|
decoration: BoxDecoration(
|
||
|
color: isSelected ? Colors.white : Colors.transparent,
|
||
|
borderRadius: BorderRadius.circular(20),
|
||
|
),
|
||
|
child: Text(
|
||
|
labels[i],
|
||
|
style: TextStyle(
|
||
|
color: isSelected ? Colors.black87 : Colors.grey.shade700,
|
||
|
fontWeight: isSelected ? FontWeight.w600 : FontWeight.w500,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
);
|
||
|
}),
|
||
|
),
|
||
|
),
|
||
|
|
||
|
const SizedBox(height: 16),
|
||
|
|
||
|
// Summary card and small stats row
|
||
|
Row(
|
||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
children: [
|
||
|
// Big card
|
||
|
Expanded(
|
||
|
flex: 2,
|
||
|
child: Container(
|
||
|
padding: const EdgeInsets.all(16),
|
||
|
decoration: BoxDecoration(
|
||
|
color: Colors.white,
|
||
|
borderRadius: BorderRadius.circular(12),
|
||
|
boxShadow: [
|
||
|
BoxShadow(color: Colors.black.withOpacity(0.03), blurRadius: 6, offset: const Offset(0, 3))
|
||
|
],
|
||
|
),
|
||
|
child: Row(
|
||
|
children: [
|
||
|
Container(
|
||
|
width: 48,
|
||
|
height: 48,
|
||
|
decoration: BoxDecoration(
|
||
|
color: const Color(0xFFF6F0FF),
|
||
|
borderRadius: BorderRadius.circular(10),
|
||
|
),
|
||
|
child: const Icon(Icons.local_shipping_outlined, color: Color(0xFF6F5FBA)),
|
||
|
),
|
||
|
const SizedBox(width: 12),
|
||
|
Expanded(
|
||
|
child: Column(
|
||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
children: const [
|
||
|
Text('Total Tankers', style: TextStyle(fontSize: 14, color: Colors.black87)),
|
||
|
SizedBox(height: 6),
|
||
|
Text('14', style: TextStyle(fontSize: 28, fontWeight: FontWeight.w700, color: Color(0xFF173F5F))),
|
||
|
SizedBox(height: 2),
|
||
|
Text('+2 since last month', style: TextStyle(fontSize: 12, color: Colors.grey)),
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
|
||
|
const SizedBox(width: 12),
|
||
|
|
||
|
// Small stat cards
|
||
|
Expanded(
|
||
|
flex: 1,
|
||
|
child: Column(
|
||
|
children: [
|
||
|
StatCard(title: 'Active', value: '2'),
|
||
|
const SizedBox(height: 8),
|
||
|
StatCard(title: 'Inactive', value: '3'),
|
||
|
const SizedBox(height: 8),
|
||
|
StatCard(title: 'Under\nMaintenances', value: '1'),
|
||
|
],
|
||
|
),
|
||
|
)
|
||
|
],
|
||
|
),
|
||
|
|
||
|
const SizedBox(height: 16),
|
||
|
|
||
|
// Search and filter row
|
||
|
Container(
|
||
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||
|
decoration: BoxDecoration(
|
||
|
color: Colors.white,
|
||
|
borderRadius: BorderRadius.circular(10),
|
||
|
),
|
||
|
child: Row(
|
||
|
children: [
|
||
|
const Icon(Icons.search, color: Colors.grey),
|
||
|
const SizedBox(width: 8),
|
||
|
Expanded(
|
||
|
child: TextField(
|
||
|
decoration: const InputDecoration(
|
||
|
hintText: 'Search',
|
||
|
border: InputBorder.none,
|
||
|
isDense: true,
|
||
|
),
|
||
|
onChanged: (v) => setState(() => search = v),
|
||
|
),
|
||
|
),
|
||
|
IconButton(
|
||
|
onPressed: () {},
|
||
|
icon: const Icon(Icons.filter_list, color: Colors.grey),
|
||
|
),
|
||
|
IconButton(
|
||
|
onPressed: () {},
|
||
|
icon: const Icon(Icons.swap_vert, color: Colors.grey),
|
||
|
)
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
|
||
|
const SizedBox(height: 12),
|
||
|
|
||
|
// List
|
||
|
Expanded(
|
||
|
child: ListView.separated(
|
||
|
itemCount: filtered.length,
|
||
|
separatorBuilder: (_, __) => const SizedBox(height: 10),
|
||
|
itemBuilder: (context, idx) {
|
||
|
final it = filtered[idx];
|
||
|
return TankCard(
|
||
|
title: it['title'],
|
||
|
subtitle: it['subtitle'],
|
||
|
code: it['code'],
|
||
|
owner: it['owner'],
|
||
|
status: List<String>.from(it['status']),
|
||
|
);
|
||
|
},
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
|
||
|
floatingActionButton: FloatingActionButton(
|
||
|
onPressed: () {},
|
||
|
backgroundColor: const Color(0xFF2E2B5F),
|
||
|
child: const Icon(Icons.add),
|
||
|
),
|
||
|
|
||
|
bottomNavigationBar: BottomNavigationBar(
|
||
|
type: BottomNavigationBarType.fixed,
|
||
|
currentIndex: 0,
|
||
|
selectedItemColor: const Color(0xFF6F5FBA),
|
||
|
unselectedItemColor: Colors.grey,
|
||
|
items: const [
|
||
|
BottomNavigationBarItem(icon: Icon(Icons.home_outlined), label: 'Home'),
|
||
|
BottomNavigationBarItem(icon: Icon(Icons.receipt_long_outlined), label: 'Orders'),
|
||
|
BottomNavigationBarItem(icon: Icon(Icons.calendar_month), label: 'Plans'),
|
||
|
BottomNavigationBarItem(icon: Icon(Icons.group_outlined), label: 'Resources'),
|
||
|
BottomNavigationBarItem(icon: Icon(Icons.menu), label: 'More'),
|
||
|
],
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class StatCard extends StatelessWidget {
|
||
|
final String title;
|
||
|
final String value;
|
||
|
const StatCard({super.key, required this.title, required this.value});
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return Container(
|
||
|
height: 58,
|
||
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||
|
decoration: BoxDecoration(
|
||
|
color: Colors.white,
|
||
|
borderRadius: BorderRadius.circular(10),
|
||
|
border: Border.all(color: Colors.grey.shade200),
|
||
|
),
|
||
|
child: Row(
|
||
|
children: [
|
||
|
Expanded(child: Text(title, style: const TextStyle(fontSize: 13, color: Colors.black87))),
|
||
|
Text(value, style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w700, color: Color(0xFF173F5F))),
|
||
|
],
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class TankCard extends StatelessWidget {
|
||
|
final String title;
|
||
|
final String subtitle;
|
||
|
final String code;
|
||
|
final String owner;
|
||
|
final List<String> status;
|
||
|
const TankCard({
|
||
|
super.key,
|
||
|
required this.title,
|
||
|
required this.subtitle,
|
||
|
required this.code,
|
||
|
required this.owner,
|
||
|
required this.status,
|
||
|
});
|
||
|
|
||
|
Color _chipColor(String s) {
|
||
|
switch (s) {
|
||
|
case 'filled':
|
||
|
return const Color(0xFFE8F7F1);
|
||
|
case 'available':
|
||
|
return const Color(0xFFE8F0FF);
|
||
|
case 'empty':
|
||
|
return const Color(0xFFFFEEEE);
|
||
|
case 'in-use':
|
||
|
return const Color(0xFFFFF0E6);
|
||
|
case 'maintenance':
|
||
|
return const Color(0xFFFFF4E6);
|
||
|
default:
|
||
|
return const Color(0xFFECECEC);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Color _chipTextColor(String s) {
|
||
|
switch (s) {
|
||
|
case 'filled':
|
||
|
return Colors.green.shade700;
|
||
|
case 'available':
|
||
|
return const Color(0xFF2E2B5F);
|
||
|
case 'empty':
|
||
|
return Colors.red.shade600;
|
||
|
case 'in-use':
|
||
|
return Colors.orange.shade700;
|
||
|
case 'maintenance':
|
||
|
return Colors.orange.shade700;
|
||
|
default:
|
||
|
return Colors.black87;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return Container(
|
||
|
padding: const EdgeInsets.all(12),
|
||
|
decoration: BoxDecoration(
|
||
|
color: Colors.white,
|
||
|
borderRadius: BorderRadius.circular(12),
|
||
|
border: Border.all(color: Colors.grey.shade100),
|
||
|
),
|
||
|
child: Row(
|
||
|
children: [
|
||
|
// left column: chips + texts
|
||
|
Expanded(
|
||
|
child: Column(
|
||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
children: [
|
||
|
// chips
|
||
|
Row(
|
||
|
children: status.map((s) {
|
||
|
return Container(
|
||
|
margin: const EdgeInsets.only(right: 6),
|
||
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||
|
decoration: BoxDecoration(
|
||
|
color: _chipColor(s),
|
||
|
borderRadius: BorderRadius.circular(8),
|
||
|
border: Border.all(color: Colors.grey.withOpacity(0.12)),
|
||
|
),
|
||
|
child: Text(
|
||
|
s,
|
||
|
style: TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: _chipTextColor(s)),
|
||
|
),
|
||
|
);
|
||
|
}).toList(),
|
||
|
),
|
||
|
const SizedBox(height: 8),
|
||
|
Text(title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w700)),
|
||
|
const SizedBox(height: 6),
|
||
|
Text(subtitle, style: const TextStyle(fontSize: 13, color: Colors.grey)),
|
||
|
const SizedBox(height: 10),
|
||
|
Row(
|
||
|
children: [
|
||
|
CircleAvatar(radius: 10, backgroundColor: const Color(0xFFEEF5FF), child: const Icon(Icons.person, size: 12, color: Color(0xFF6F5FBA))),
|
||
|
const SizedBox(width: 6),
|
||
|
Expanded(child: Text(owner, style: const TextStyle(fontSize: 12, color: Colors.grey))),
|
||
|
],
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
|
||
|
// right column: code
|
||
|
Column(
|
||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||
|
children: [
|
||
|
Text(code, style: const TextStyle(fontSize: 12, color: Colors.grey)),
|
||
|
const SizedBox(height: 28),
|
||
|
],
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
}
|