Initial commit
This commit is contained in:
124
lib/features/support/screens/chat_screen.dart
Normal file
124
lib/features/support/screens/chat_screen.dart
Normal 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),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
258
lib/features/support/screens/help_support_screen.dart
Normal file
258
lib/features/support/screens/help_support_screen.dart
Normal 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),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user