457 lines
22 KiB
Dart
457 lines
22 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import '../../core/app_colors.dart';
|
|
import '../../services/auth_service.dart';
|
|
import '../../services/route_simulator_service.dart';
|
|
import '../../database/db_helper.dart';
|
|
import '../../models/models.dart';
|
|
import '../../data/routes_data.dart';
|
|
import '../../widgets/route_map_widget.dart';
|
|
import '../settings_screen.dart';
|
|
|
|
class DriverHomeScreen extends StatefulWidget {
|
|
const DriverHomeScreen({super.key});
|
|
@override State<DriverHomeScreen> createState() => _DriverHomeScreenState();
|
|
}
|
|
|
|
class _DriverHomeScreenState extends State<DriverHomeScreen> {
|
|
int _tab = 0;
|
|
List<AssignmentModel> _assignments = [];
|
|
String? _todayRouteId;
|
|
|
|
@override void initState() { super.initState(); _load(); }
|
|
|
|
Future<void> _load() async {
|
|
final auth = context.read<AuthService>();
|
|
if (auth.currentUser == null) return;
|
|
final list = await DbHelper.getAsignacionesByConductor(auth.currentUser!.id!);
|
|
final today = _todayDia();
|
|
setState(() {
|
|
_assignments = list;
|
|
final match = list.where((a) => a.diaSemana == today);
|
|
_todayRouteId = match.isNotEmpty ? match.first.routeId : null;
|
|
});
|
|
if (_todayRouteId != null) {
|
|
context.read<RouteSimulatorService>().startRoute(_todayRouteId!);
|
|
}
|
|
}
|
|
|
|
String _todayDia() {
|
|
const d = ['','LUNES','MARTES','MIERCOLES','JUEVES','VIERNES','SABADO','DOMINGO'];
|
|
return d[DateTime.now().weekday];
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final auth = context.watch<AuthService>();
|
|
final sim = context.watch<RouteSimulatorService>();
|
|
final route = _todayRouteId != null ? getRouteById(_todayRouteId!) : null;
|
|
|
|
// Solo notificaciones de la ruta actual del conductor
|
|
final lastNotif = _todayRouteId != null
|
|
? sim.getNotificationForRoute(_todayRouteId!) : null;
|
|
|
|
final tabs = [
|
|
_DriverMainTab(auth:auth, sim:sim, route:route,
|
|
assignments:_assignments, todayRouteId:_todayRouteId, onRefresh:_load),
|
|
if (route != null) _DriverMapTab(route:route, sim:sim)
|
|
else const Center(child:Text('Sin ruta hoy')),
|
|
_DriverReportesTab(conductorId:auth.currentUser?.id, todayRouteId:_todayRouteId),
|
|
];
|
|
|
|
return Scaffold(
|
|
body: Stack(children:[
|
|
tabs[_tab],
|
|
if (lastNotif != null)
|
|
Positioned(top:MediaQuery.of(context).padding.top+8, left:0, right:0,
|
|
child:_NotifBanner(notif:lastNotif,
|
|
onDismiss:()=>sim.dismissRouteNotification(_todayRouteId??''))),
|
|
]),
|
|
bottomNavigationBar: NavigationBar(
|
|
selectedIndex: _tab,
|
|
onDestinationSelected: (i) => setState(()=>_tab=i),
|
|
backgroundColor: Colors.white,
|
|
indicatorColor: AppColors.guindaPrimary.withOpacity(0.15),
|
|
destinations: const [
|
|
NavigationDestination(icon:Icon(Icons.dashboard_outlined),
|
|
selectedIcon:Icon(Icons.dashboard,color:AppColors.guindaPrimary),label:'Mi Ruta'),
|
|
NavigationDestination(icon:Icon(Icons.map_outlined),
|
|
selectedIcon:Icon(Icons.map,color:AppColors.guindaPrimary),label:'Mapa'),
|
|
NavigationDestination(icon:Icon(Icons.report_problem_outlined),
|
|
selectedIcon:Icon(Icons.report_problem,color:AppColors.guindaPrimary),label:'Incidente'),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
// ── Tab principal ─────────────────────────────────────────────────────────
|
|
class _DriverMainTab extends StatefulWidget {
|
|
final AuthService auth; final RouteSimulatorService sim;
|
|
final route; final assignments; final todayRouteId; final VoidCallback onRefresh;
|
|
const _DriverMainTab({required this.auth, required this.sim, required this.route,
|
|
required this.assignments, required this.todayRouteId, required this.onRefresh});
|
|
@override State<_DriverMainTab> createState() => _DriverMainTabState();
|
|
}
|
|
|
|
class _DriverMainTabState extends State<_DriverMainTab> {
|
|
List<ReporteModel> _ciudadanoReportes = [];
|
|
|
|
@override void initState() { super.initState(); _loadReportes(); }
|
|
|
|
Future<void> _loadReportes() async {
|
|
if (widget.todayRouteId == null) return;
|
|
final all = await DbHelper.getAllReportes();
|
|
final filtered = all.where((r) => r.routeId == widget.todayRouteId).toList();
|
|
if (mounted) setState(() => _ciudadanoReportes = filtered.take(5).toList());
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final posIdx = widget.todayRouteId != null
|
|
? widget.sim.getPositionIndex(widget.todayRouteId!) : 0;
|
|
final gpsOk = widget.todayRouteId != null
|
|
? widget.sim.isGpsActive(widget.todayRouteId!) : true;
|
|
|
|
return CustomScrollView(slivers:[
|
|
SliverAppBar(pinned:true, backgroundColor:AppColors.guindaPrimary, foregroundColor:Colors.white,
|
|
bottom:PreferredSize(preferredSize:const Size.fromHeight(4),
|
|
child:Container(height:4,color:AppColors.dorado)),
|
|
title:Text('Conductor: ${widget.auth.currentUser?.nombre.split(' ').first ?? ''}',
|
|
style:const TextStyle(fontSize:16,fontWeight:FontWeight.bold)),
|
|
actions:[IconButton(icon:const Icon(Icons.logout),
|
|
onPressed:()async{ await widget.auth.logout();
|
|
if(context.mounted) Navigator.pushReplacementNamed(context,'/login');})]),
|
|
|
|
SliverPadding(padding:const EdgeInsets.all(14),sliver:SliverList(delegate:SliverChildListDelegate([
|
|
// Ruta de hoy
|
|
Card(color:AppColors.guindaPrimary.withOpacity(0.08),
|
|
shape:RoundedRectangleBorder(borderRadius:BorderRadius.circular(12),
|
|
side:BorderSide(color:AppColors.guindaPrimary.withOpacity(0.3))),
|
|
child:Padding(padding:const EdgeInsets.all(14),child:Column(
|
|
crossAxisAlignment:CrossAxisAlignment.start, children:[
|
|
Row(children:[
|
|
const Icon(Icons.today,color:AppColors.guindaPrimary),
|
|
const SizedBox(width:8),
|
|
Text('Hoy — ${_todayLabel()}',
|
|
style:const TextStyle(fontWeight:FontWeight.bold,color:AppColors.guindaPrimary,fontSize:15)),
|
|
]),
|
|
const Divider(),
|
|
if (widget.route != null)...[
|
|
Text(widget.route.name,style:const TextStyle(fontWeight:FontWeight.bold,fontSize:14)),
|
|
Text('Camión ${widget.route.truckId} • Turno: ${widget.route.turno}',
|
|
style:const TextStyle(color:AppColors.grisTexto,fontSize:12)),
|
|
const SizedBox(height:8),
|
|
Row(children:[
|
|
Icon(gpsOk?Icons.gps_fixed:Icons.gps_off,
|
|
color:gpsOk?AppColors.verdeExito:AppColors.rojoError,size:16),
|
|
const SizedBox(width:4),
|
|
Text(gpsOk?'GPS Activo':' GPS Desactivado',
|
|
style:TextStyle(color:gpsOk?AppColors.verdeExito:AppColors.rojoError,
|
|
fontWeight:FontWeight.bold,fontSize:12)),
|
|
const Spacer(),
|
|
Text('Posición ${posIdx+1}/8',style:const TextStyle(color:AppColors.grisTexto,fontSize:12)),
|
|
]),
|
|
const SizedBox(height:8),
|
|
LinearProgressIndicator(value:(posIdx+1)/8,
|
|
backgroundColor:Colors.grey.shade300,
|
|
valueColor:const AlwaysStoppedAnimation<Color>(AppColors.guindaPrimary)),
|
|
const SizedBox(height:6),
|
|
Text(widget.sim.getEtaText(widget.todayRouteId??''),
|
|
style:const TextStyle(fontSize:13,fontWeight:FontWeight.w500)),
|
|
] else
|
|
const Text(' Sin ruta asignada hoy.',style:TextStyle(color:AppColors.rojoError)),
|
|
]))),
|
|
const SizedBox(height:10),
|
|
|
|
// Instrucciones
|
|
Card(child:Padding(padding:const EdgeInsets.all(12),child:Column(
|
|
crossAxisAlignment:CrossAxisAlignment.start, children:[
|
|
const Text(' Instrucciones de Ruta',
|
|
style:TextStyle(fontWeight:FontWeight.bold,color:AppColors.guindaPrimary)),
|
|
const Divider(),
|
|
const Text('• Sigue la ruta asignada sin desviaciones\n'
|
|
'• Mantén el GPS activo en todo momento\n'
|
|
'• Reporta incidentes desde "Incidente"\n'
|
|
'• Si hay problema, el admin decidirá si se cancela o retrasa',
|
|
style:TextStyle(fontSize:12,color:AppColors.grisTexto)),
|
|
]))),
|
|
const SizedBox(height:10),
|
|
|
|
// Reportes ciudadanos de SU ruta
|
|
if (_ciudadanoReportes.isNotEmpty) ...[
|
|
Card(color:Colors.orange.shade50,
|
|
shape:RoundedRectangleBorder(borderRadius:BorderRadius.circular(10),
|
|
side:BorderSide(color:Colors.orange.shade200)),
|
|
child:Padding(padding:const EdgeInsets.all(12),child:Column(
|
|
crossAxisAlignment:CrossAxisAlignment.start, children:[
|
|
const Row(children:[
|
|
Icon(Icons.people,color:AppColors.naranjaAlerta,size:16),
|
|
SizedBox(width:6),
|
|
Text('Reportes de tu ruta hoy',
|
|
style:TextStyle(fontWeight:FontWeight.bold,color:AppColors.naranjaAlerta,fontSize:13)),
|
|
]),
|
|
const Divider(),
|
|
..._ciudadanoReportes.map((r)=>Padding(
|
|
padding:const EdgeInsets.symmetric(vertical:3),
|
|
child:Row(children:[
|
|
const Icon(Icons.person_outline,size:12,color:AppColors.grisTexto),
|
|
const SizedBox(width:4),
|
|
Expanded(child:Text(r.descripcion,style:const TextStyle(fontSize:11),
|
|
maxLines:1,overflow:TextOverflow.ellipsis)),
|
|
]))),
|
|
]))),
|
|
const SizedBox(height:10),
|
|
],
|
|
|
|
// Horario LMV / MJS
|
|
Card(child:Padding(padding:const EdgeInsets.all(12),child:Column(
|
|
crossAxisAlignment:CrossAxisAlignment.start, children:[
|
|
const Text('Mi Horario',
|
|
style:TextStyle(fontWeight:FontWeight.bold,color:AppColors.guindaPrimary)),
|
|
const Divider(),
|
|
if (widget.assignments.isEmpty)
|
|
const Text('Sin asignaciones. Contacta al administrador.',
|
|
style:TextStyle(color:AppColors.grisTexto,fontSize:12))
|
|
else ...[
|
|
_scheduleGroup(widget.assignments,'LUNES','MIERCOLES','VIERNES',
|
|
'Lunes, Miércoles y Viernes'),
|
|
const SizedBox(height:8),
|
|
_scheduleGroup(widget.assignments,'MARTES','JUEVES','SABADO',
|
|
'Martes, Jueves y Sábado'),
|
|
],
|
|
]))),
|
|
const SizedBox(height:80),
|
|
]))),
|
|
]);
|
|
}
|
|
|
|
Widget _scheduleGroup(List<AssignmentModel> all, String d1, String d2, String d3, String label) {
|
|
AssignmentModel? found;
|
|
for (final dia in [d1,d2,d3]) {
|
|
try { found = all.firstWhere((a)=>a.diaSemana==dia); break; } catch(_){}
|
|
}
|
|
return Container(padding:const EdgeInsets.all(10),
|
|
decoration:BoxDecoration(color:AppColors.guindaPrimary.withOpacity(0.06),
|
|
borderRadius:BorderRadius.circular(8),
|
|
border:Border.all(color:AppColors.guindaPrimary.withOpacity(0.2))),
|
|
child:Row(children:[
|
|
const Icon(Icons.calendar_today,size:14,color:AppColors.guindaPrimary),
|
|
const SizedBox(width:6),
|
|
Expanded(child:Text(label,style:const TextStyle(fontWeight:FontWeight.w600,fontSize:12))),
|
|
if (found!=null)
|
|
Container(padding:const EdgeInsets.symmetric(horizontal:8,vertical:3),
|
|
decoration:BoxDecoration(color:AppColors.guindaPrimary,borderRadius:BorderRadius.circular(8)),
|
|
child:Text('${found.routeId} • ${found.turno}',
|
|
style:const TextStyle(fontSize:11,color:Colors.white,fontWeight:FontWeight.bold)))
|
|
else
|
|
const Text('Sin asignar',style:TextStyle(fontSize:11,color:AppColors.grisTexto)),
|
|
]));
|
|
}
|
|
|
|
String _todayLabel() {
|
|
const d=['','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado','Domingo'];
|
|
return d[DateTime.now().weekday];
|
|
}
|
|
}
|
|
|
|
// ── Tab mapa ──────────────────────────────────────────────────────────────
|
|
class _DriverMapTab extends StatelessWidget {
|
|
final route; final sim;
|
|
const _DriverMapTab({required this.route, required this.sim});
|
|
@override
|
|
Widget build(BuildContext context) => Scaffold(
|
|
appBar:AppBar(automaticallyImplyLeading:false,
|
|
backgroundColor:AppColors.guindaPrimary,foregroundColor:Colors.white,
|
|
title:Text(route.name,style:const TextStyle(fontSize:13)),
|
|
bottom:PreferredSize(preferredSize:const Size.fromHeight(4),
|
|
child:Container(height:4,color:AppColors.dorado))),
|
|
body:DriverRouteMap(route:route,simulator:sim));
|
|
}
|
|
|
|
// ── Tab reporte incidente — usa routeId actual ────────────────────────────
|
|
class _DriverReportesTab extends StatefulWidget {
|
|
final int? conductorId;
|
|
final String? todayRouteId; // Ruta actual del conductor
|
|
const _DriverReportesTab({required this.conductorId, required this.todayRouteId});
|
|
@override State<_DriverReportesTab> createState() => _DriverReportesTabState();
|
|
}
|
|
|
|
class _DriverReportesTabState extends State<_DriverReportesTab> {
|
|
String _tipo = 'INCIDENTE_LLANTA';
|
|
final _desc = TextEditingController();
|
|
bool _loading = false, _sent = false;
|
|
List<AlertaModel> _misIncidentes = [];
|
|
|
|
static const _tipos = {
|
|
'INCIDENTE_LLANTA': ' Llanta ponchada',
|
|
'INCIDENTE_MECANICA': ' Falla mecánica',
|
|
'INCIDENTE_ACCIDENTE': ' Accidente',
|
|
'INCIDENTE_CAMINO': ' Camino bloqueado',
|
|
'INCIDENTE_COMBUSTIBLE':' Sin combustible',
|
|
'INCIDENTE_OTRO': 'Otro',
|
|
};
|
|
|
|
@override void initState() { super.initState(); _load(); }
|
|
|
|
Future<void> _load() async {
|
|
final all = await DbHelper.getAlertas();
|
|
// Solo incidentes de la ruta actual del conductor
|
|
final mine = all.where((a) =>
|
|
a.tipo.startsWith('INCIDENTE_') &&
|
|
a.routeId == (widget.todayRouteId ?? '')).toList();
|
|
if (mounted) setState(() => _misIncidentes = mine);
|
|
}
|
|
|
|
Future<void> _enviar() async {
|
|
if (widget.todayRouteId == null) {
|
|
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
|
content:Text('No tienes ruta asignada hoy'),
|
|
backgroundColor:AppColors.rojoError)); return;
|
|
}
|
|
if (_desc.text.trim().isEmpty) {
|
|
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
|
content:Text('Describe el incidente'),backgroundColor:AppColors.rojoError)); return;
|
|
}
|
|
setState(()=>_loading=true);
|
|
// Guardar el incidente asociado a la RUTA ACTUAL
|
|
await DbHelper.insertAlerta(AlertaModel(
|
|
tipo: _tipo,
|
|
routeId: widget.todayRouteId!, // ← ID de la ruta actual, no del conductor
|
|
mensaje: '${_tipos[_tipo]}: ${_desc.text.trim()}',
|
|
fecha: DateTime.now().toIso8601String(),
|
|
));
|
|
await _load();
|
|
if (!mounted) return;
|
|
setState(() { _loading=false; _sent=true; });
|
|
_desc.clear();
|
|
await Future.delayed(const Duration(seconds:2));
|
|
if (mounted) setState(()=>_sent=false);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) => Scaffold(
|
|
backgroundColor:AppColors.grisFondo,
|
|
appBar:AppBar(automaticallyImplyLeading:false,
|
|
backgroundColor:AppColors.guindaPrimary,foregroundColor:Colors.white,
|
|
title:const Text('Reportar Incidente'),
|
|
bottom:PreferredSize(preferredSize:const Size.fromHeight(4),
|
|
child:Container(height:4,color:AppColors.dorado))),
|
|
body: _sent
|
|
? const Center(child:Column(mainAxisAlignment:MainAxisAlignment.center,children:[
|
|
Icon(Icons.check_circle,color:AppColors.verdeExito,size:64),
|
|
SizedBox(height:12),
|
|
Text('¡Incidente reportado!',style:TextStyle(fontSize:18,fontWeight:FontWeight.bold,color:AppColors.verdeExito)),
|
|
Text('El administrador será notificado.',style:TextStyle(color:AppColors.grisTexto)),
|
|
]))
|
|
: SingleChildScrollView(padding:const EdgeInsets.all(16),child:Column(children:[
|
|
// Info ruta actual
|
|
if (widget.todayRouteId != null)
|
|
Container(margin:const EdgeInsets.only(bottom:12),
|
|
padding:const EdgeInsets.all(10),
|
|
decoration:BoxDecoration(color:AppColors.guindaPrimary.withOpacity(0.08),
|
|
borderRadius:BorderRadius.circular(8),
|
|
border:Border.all(color:AppColors.guindaPrimary.withOpacity(0.3))),
|
|
child:Row(children:[
|
|
const Icon(Icons.route,color:AppColors.guindaPrimary,size:16),
|
|
const SizedBox(width:6),
|
|
Text('Incidente en: ${widget.todayRouteId}',
|
|
style:const TextStyle(fontWeight:FontWeight.bold,color:AppColors.guindaPrimary,fontSize:13)),
|
|
]))
|
|
else
|
|
Container(margin:const EdgeInsets.only(bottom:12),
|
|
padding:const EdgeInsets.all(10),
|
|
decoration:BoxDecoration(color:Colors.orange.shade50,borderRadius:BorderRadius.circular(8)),
|
|
child:const Text(' No tienes ruta asignada hoy',
|
|
style:TextStyle(color:AppColors.naranjaAlerta,fontWeight:FontWeight.bold))),
|
|
|
|
Card(shape:RoundedRectangleBorder(borderRadius:BorderRadius.circular(12)),
|
|
child:Padding(padding:const EdgeInsets.all(16),child:Column(
|
|
crossAxisAlignment:CrossAxisAlignment.start, children:[
|
|
const Text('Tipo de incidente',
|
|
style:TextStyle(fontWeight:FontWeight.bold,color:AppColors.guindaPrimary,fontSize:15)),
|
|
const SizedBox(height:8),
|
|
..._tipos.entries.map((e)=>RadioListTile<String>(dense:true,
|
|
value:e.key,groupValue:_tipo,
|
|
title:Text(e.value,style:const TextStyle(fontSize:13)),
|
|
activeColor:AppColors.guindaPrimary,
|
|
onChanged:(v)=>setState(()=>_tipo=v!))),
|
|
const SizedBox(height:10),
|
|
const Text('Descripción',style:TextStyle(fontWeight:FontWeight.w600,fontSize:13)),
|
|
const SizedBox(height:6),
|
|
TextField(controller:_desc,maxLines:3,
|
|
decoration:const InputDecoration(hintText:'Describe qué pasó...',
|
|
border:OutlineInputBorder(),filled:true,fillColor:Colors.white)),
|
|
const SizedBox(height:12),
|
|
Container(padding:const EdgeInsets.all(10),
|
|
decoration:BoxDecoration(color:Colors.orange.shade50,
|
|
borderRadius:BorderRadius.circular(8),
|
|
border:Border.all(color:Colors.orange.shade200)),
|
|
child:const Text(
|
|
' El administrador verá este incidente en tu ruta actual '
|
|
'y decidirá si continúa, se retrasa o se cancela.',
|
|
style:TextStyle(fontSize:11,color:Colors.black87))),
|
|
const SizedBox(height:14),
|
|
SizedBox(width:double.infinity,height:48,
|
|
child:ElevatedButton.icon(
|
|
onPressed:(_loading||widget.todayRouteId==null)?null:_enviar,
|
|
style:ElevatedButton.styleFrom(backgroundColor:AppColors.guindaPrimary,
|
|
foregroundColor:Colors.white,
|
|
shape:RoundedRectangleBorder(borderRadius:BorderRadius.circular(8))),
|
|
icon:_loading?const SizedBox(width:18,height:18,
|
|
child:CircularProgressIndicator(color:Colors.white,strokeWidth:2))
|
|
:const Icon(Icons.send),
|
|
label:const Text('ENVIAR INCIDENTE',style:TextStyle(fontWeight:FontWeight.bold)))),
|
|
]))),
|
|
|
|
if (_misIncidentes.isNotEmpty)...[
|
|
const SizedBox(height:16),
|
|
const Align(alignment:Alignment.centerLeft,
|
|
child:Text('Mis incidentes de hoy',
|
|
style:TextStyle(fontWeight:FontWeight.bold,color:AppColors.guindaPrimary,fontSize:14))),
|
|
const SizedBox(height:8),
|
|
..._misIncidentes.take(5).map((a)=>Card(margin:const EdgeInsets.only(bottom:6),
|
|
child:ListTile(dense:true,
|
|
leading:CircleAvatar(backgroundColor:AppColors.guindaPrimary,radius:16,
|
|
child:const Icon(Icons.warning,color:Colors.white,size:14)),
|
|
title:Text(_tipos[a.tipo]??a.tipo,
|
|
style:const TextStyle(fontSize:12,fontWeight:FontWeight.w600)),
|
|
subtitle:Text(a.mensaje,maxLines:1,overflow:TextOverflow.ellipsis,
|
|
style:const TextStyle(fontSize:11)),
|
|
trailing:Icon(a.resuelta?Icons.check_circle:Icons.pending,
|
|
color:a.resuelta?AppColors.verdeExito:AppColors.naranjaAlerta,size:18)))),
|
|
],
|
|
])),
|
|
);
|
|
|
|
@override void dispose(){ _desc.dispose(); super.dispose(); }
|
|
}
|
|
|
|
// ── Notif banner conductor ────────────────────────────────────────────────
|
|
class _NotifBanner extends StatelessWidget {
|
|
final AppNotification notif; final VoidCallback onDismiss;
|
|
const _NotifBanner({required this.notif, required this.onDismiss});
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final color = notif.event==NotifEvent.gpsLost?Colors.red.shade800
|
|
:notif.event==NotifEvent.truckStopped?AppColors.naranjaAlerta
|
|
:notif.event==NotifEvent.routeCancelled?AppColors.rojoError
|
|
:AppColors.guindaPrimary;
|
|
return Material(color:Colors.transparent,
|
|
child:Container(margin:const EdgeInsets.all(10),
|
|
decoration:BoxDecoration(color:color,borderRadius:BorderRadius.circular(12),
|
|
boxShadow:const[BoxShadow(color:Colors.black26,blurRadius:6)]),
|
|
child:Padding(padding:const EdgeInsets.all(12),child:Row(children:[
|
|
const Icon(Icons.notification_important,color:Colors.white,size:22),
|
|
const SizedBox(width:8),
|
|
Expanded(child:Column(crossAxisAlignment:CrossAxisAlignment.start,
|
|
mainAxisSize:MainAxisSize.min,children:[
|
|
Text(notif.title,style:const TextStyle(color:Colors.white,fontWeight:FontWeight.bold,fontSize:13)),
|
|
Text(notif.body,style:const TextStyle(color:Colors.white70,fontSize:11),
|
|
maxLines:2,overflow:TextOverflow.ellipsis),
|
|
])),
|
|
IconButton(icon:const Icon(Icons.close,color:Colors.white,size:18),onPressed:onDismiss),
|
|
]))));
|
|
}
|
|
}
|