Initial commit

This commit is contained in:
2026-06-23 11:29:07 +05:30
commit 401cf600e8
224 changed files with 12165 additions and 0 deletions

View File

@@ -0,0 +1,124 @@
import 'package:flutter/material.dart';
import 'package:doormile/core/theme/app_colors.dart';
import 'package:doormile/core/theme/app_theme.dart';
import 'package:doormile/core/widgets/common.dart';
class ChatScreen extends StatefulWidget {
final String riderName;
const ChatScreen({super.key, required this.riderName});
@override
State<ChatScreen> createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final TextEditingController _messageController = TextEditingController();
final List<String> _messages = [
'Hi! I am picking up your parcel now.',
'Please let me know if there are any specific instructions.',
];
@override
void dispose() {
_messageController.dispose();
super.dispose();
}
void _sendMessage() {
if (_messageController.text.trim().isEmpty) return;
setState(() {
_messages.add(_messageController.text.trim());
_messageController.clear();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.surface,
appBar: AppBar(
backgroundColor: AppColors.primary,
foregroundColor: Colors.white,
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(widget.riderName, style: AppTheme.headlineSm.copyWith(color: Colors.white, fontSize: 18)),
Text('Doormile EV Rider', style: AppTheme.caption.copyWith(color: Colors.white70)),
],
),
),
body: Column(
children: [
Expanded(
child: ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: _messages.length,
itemBuilder: (context, index) {
final isRider = index < 2; // Mocking first 2 messages from rider
return Align(
alignment: isRider ? Alignment.centerLeft : Alignment.centerRight,
child: Container(
margin: const EdgeInsets.only(bottom: 12),
constraints: const BoxConstraints(maxWidth: 280),
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: isRider ? AppColors.surfaceContainerHigh : AppColors.primary,
borderRadius: BorderRadius.circular(16).copyWith(
bottomLeft: isRider ? Radius.zero : const Radius.circular(16),
bottomRight: !isRider ? Radius.zero : const Radius.circular(16),
),
),
child: Text(
_messages[index],
style: AppTheme.bodyMd.copyWith(color: isRider ? AppColors.onSurface : Colors.white),
),
),
);
},
),
),
Container(
padding: const EdgeInsets.all(16).copyWith(bottom: MediaQuery.of(context).padding.bottom + 16),
decoration: const BoxDecoration(
color: Colors.white,
border: Border(top: BorderSide(color: AppColors.surfaceContainer)),
),
child: Row(
children: [
Expanded(
child: TextField(
controller: _messageController,
decoration: InputDecoration(
hintText: 'Message ${widget.riderName}...',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(999),
borderSide: BorderSide.none,
),
filled: true,
fillColor: AppColors.surfaceContainerLowest,
contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
),
),
),
const SizedBox(width: 12),
InkWell(
onTap: _sendMessage,
borderRadius: BorderRadius.circular(999),
child: Container(
padding: const EdgeInsets.all(14),
decoration: const BoxDecoration(
color: AppColors.primary,
shape: BoxShape.circle,
),
child: const Icon(Icons.send_rounded, color: Colors.white, size: 20),
),
),
],
),
),
],
),
);
}
}

View File

@@ -0,0 +1,258 @@
import 'package:flutter/material.dart';
import 'package:doormile/shared/models/models.dart';
import 'package:doormile/core/theme/app_colors.dart';
import 'package:doormile/core/theme/app_theme.dart';
import 'package:doormile/core/widgets/common.dart';
class HelpSupportScreen extends StatefulWidget {
const HelpSupportScreen({super.key});
@override
State<HelpSupportScreen> createState() => _HelpSupportScreenState();
}
class _HelpSupportScreenState extends State<HelpSupportScreen> {
final _faqs = const [
FaqItem('How do I track?',
'Track your delivery in real-time through the "Track" tab in the bottom navigation. Our EV fleet provides precision GPS updates every 10 seconds.'),
FaqItem('Price calculation?',
'Fare = base fare + distance charge + any demand/weather surge. EV sustainability discounts are applied automatically at checkout.'),
FaqItem('Cancellation?',
'You can cancel free of charge before a rider is assigned. After assignment, a small fee may apply to cover the EV trip.'),
];
final List<ChatMessage> _messages = [
const ChatMessage("Hi there! I'm Doormile Assistant. How can I help you today with your EV delivery?"),
const ChatMessage('I need to update my delivery address for order #DM-9921.', fromUser: true),
const ChatMessage('Sure! Our system shows your EV driver is still 10 mins away from pickup. I can update that for you now.'),
];
final _input = TextEditingController();
int? _openFaq = 0;
void _send() {
final text = _input.text.trim();
if (text.isEmpty) return;
setState(() {
_messages.add(ChatMessage(text, fromUser: true));
_input.clear();
});
Future.delayed(const Duration(milliseconds: 700), () {
if (!mounted) return;
setState(() => _messages.add(const ChatMessage(
'Thanks! A Doormile specialist will follow up shortly. Is there anything else?')));
});
}
@override
void dispose() {
_input.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.surface,
appBar: AppBar(
leading: BackButton(color: AppColors.primary, onPressed: () => Navigator.pop(context)),
title: Text('Help & Support',
style: AppTheme.headlineMd.copyWith(color: AppColors.primary)),
actions: const [
Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.help_outline, color: AppColors.primary),
),
],
),
body: ListView(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 24),
children: [
// Search
Container(
padding: const EdgeInsets.symmetric(horizontal: 14),
decoration: BoxDecoration(
color: AppColors.surfaceContainerLowest,
borderRadius: BorderRadius.circular(14),
boxShadow: AppTheme.cardShadow,
),
child: Row(
children: [
const Icon(Icons.search, color: AppColors.onSurfaceVariant),
const SizedBox(width: 8),
Expanded(
child: TextField(
cursorColor: AppColors.primary,
decoration: InputDecoration(
hintText: 'Search for help...',
hintStyle: AppTheme.bodyMd.copyWith(color: AppColors.outline),
border: InputBorder.none,
),
),
),
],
),
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Text('Frequently Asked Questions',
style: AppTheme.headlineSm, overflow: TextOverflow.ellipsis),
),
const SizedBox(width: 8),
Text('View all',
style: AppTheme.labelMd.copyWith(color: AppColors.primary)),
],
),
const SizedBox(height: 12),
...List.generate(_faqs.length, (i) {
final open = _openFaq == i;
return Padding(
padding: const EdgeInsets.only(bottom: 10),
child: AppCard(
onTap: () => setState(() => _openFaq = open ? null : i),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(_faqs[i].question,
style: AppTheme.labelMd.copyWith(fontWeight: FontWeight.w700)),
),
Icon(open ? Icons.expand_less : Icons.expand_more,
color: AppColors.onSurfaceVariant),
],
),
AnimatedCrossFade(
duration: const Duration(milliseconds: 200),
crossFadeState:
open ? CrossFadeState.showFirst : CrossFadeState.showSecond,
firstChild: Padding(
padding: const EdgeInsets.only(top: 8),
child: Text(_faqs[i].answer,
style: AppTheme.bodyMd
.copyWith(color: AppColors.onSurfaceVariant, height: 1.4)),
),
secondChild: const SizedBox(width: double.infinity),
),
],
),
),
);
}),
const SizedBox(height: 16),
Text('Recent Support', style: AppTheme.headlineSm),
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: AppColors.surfaceContainer,
borderRadius: BorderRadius.circular(16),
),
child: Column(
children: [
..._messages.map(_bubble),
const SizedBox(height: 8),
_composer(),
],
),
),
const SizedBox(height: 16),
// Chat with us banner
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColors.primaryContainer,
borderRadius: BorderRadius.circular(16),
),
child: Row(
children: [
Container(
width: 44,
height: 44,
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(12)),
child: const Icon(Icons.chat, color: Colors.white),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Chat with us',
style: AppTheme.labelMd.copyWith(
color: Colors.white, fontWeight: FontWeight.w700)),
Text('Get instant support from our team',
style: AppTheme.caption
.copyWith(color: Colors.white.withValues(alpha: 0.9))),
],
),
),
const Icon(Icons.chevron_right, color: Colors.white),
],
),
),
],
),
);
}
Widget _bubble(ChatMessage m) {
return Align(
alignment: m.fromUser ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
margin: const EdgeInsets.symmetric(vertical: 4),
padding: const EdgeInsets.all(12),
constraints: const BoxConstraints(maxWidth: 260),
decoration: BoxDecoration(
color: m.fromUser ? AppColors.primary : AppColors.surfaceContainerLowest,
borderRadius: BorderRadius.circular(14),
),
child: Text(m.text,
style: AppTheme.bodyMd.copyWith(
color: m.fromUser ? Colors.white : AppColors.onSurface, height: 1.35)),
),
);
}
Widget _composer() {
return Container(
padding: const EdgeInsets.only(left: 14, right: 6),
decoration: BoxDecoration(
color: AppColors.surfaceContainerLowest,
borderRadius: BorderRadius.circular(999),
),
child: Row(
children: [
Expanded(
child: TextField(
controller: _input,
cursorColor: AppColors.primary,
onSubmitted: (_) => _send(),
decoration: InputDecoration(
hintText: 'Type a message...',
hintStyle: AppTheme.bodyMd.copyWith(color: AppColors.outline),
border: InputBorder.none,
),
),
),
GestureDetector(
onTap: _send,
child: Container(
width: 40,
height: 40,
decoration: const BoxDecoration(
color: AppColors.primary, shape: BoxShape.circle),
child: const Icon(Icons.send, color: Colors.white, size: 18),
),
),
],
),
);
}
}