122 lines
6.0 KiB
Dart
122 lines
6.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import '../../core/app_colors.dart';
|
|
|
|
class OnboardingScreen extends StatefulWidget {
|
|
const OnboardingScreen({super.key});
|
|
@override State<OnboardingScreen> createState() => _OnboardingScreenState();
|
|
}
|
|
|
|
class _OnboardingScreenState extends State<OnboardingScreen> {
|
|
final _ctrl = PageController();
|
|
int _page = 0;
|
|
|
|
static const _pages = [
|
|
_OnboardPage(icon:Icons.delete_sweep_rounded, color:AppColors.guindaPrimary,
|
|
title:'Bienvenido a Celaya Limpia',
|
|
subtitle:'El sistema de recoleccion inteligente del Direccion General de Servicios Municipales de Celaya.',
|
|
desc:'Recibe alertas en tiempo real, conoce tu horario y ayuda a mantener tu ciudad limpia.'),
|
|
_OnboardPage(icon:Icons.notifications_active, color:AppColors.azulInfo,
|
|
title:'Alertas inteligentes',
|
|
subtitle:'Te avisamos exactamente cuando debes sacar tu basura.',
|
|
desc:'Recibiras 3 alertas:\n\n30 min antes: el camion esta en camino.\n15 min antes: saca tus bolsas a la acera.\nAl pasar: confirma que fue recogida.\n\nNunca mas pierdas al camion recolector.'),
|
|
_OnboardPage(icon:Icons.recycling, color:AppColors.verdeExito,
|
|
title:'Guia de separacion',
|
|
subtitle:'Aprende a separar correctamente tus residuos.',
|
|
desc:'Bolsa verde: organicos (comida, jardin)\nBolsa azul: reciclables (PET, latas)\nBolsa negra: no reciclables\n\nUsa la camara IA para identificar si un residuo es organico o inorganico al instante.'),
|
|
_OnboardPage(icon:Icons.star, color:Colors.amber,
|
|
title:'Tu opinion importa',
|
|
subtitle:'Califica el servicio y ayuda a mejorarlo.',
|
|
desc:'Despues de cada recoleccion podras:\n\nCalificar de 1 a 5 estrellas\nDejar comentarios a la Direccion General\nReportar incidencias con foto\n\nTus reportes son atendidos por el equipo municipal.'),
|
|
];
|
|
|
|
Future<void> _finish() async {
|
|
final p = await SharedPreferences.getInstance();
|
|
await p.setBool('onboarding_done', true);
|
|
if (!mounted) return;
|
|
Navigator.pushReplacementNamed(context, '/login');
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
body: Stack(children: [
|
|
PageView.builder(controller:_ctrl, onPageChanged:(p)=>setState(()=>_page=p),
|
|
itemCount:_pages.length,
|
|
itemBuilder:(_,i)=>_PageContent(page:_pages[i])),
|
|
Positioned(bottom:120, left:0, right:0,
|
|
child:Row(mainAxisAlignment:MainAxisAlignment.center,
|
|
children:List.generate(_pages.length,(i)=>AnimatedContainer(
|
|
duration:const Duration(milliseconds:250),
|
|
margin:const EdgeInsets.symmetric(horizontal:4),
|
|
width:_page==i?24:8, height:8,
|
|
decoration:BoxDecoration(
|
|
color:_page==i?_pages[i].color:Colors.grey.shade300,
|
|
borderRadius:BorderRadius.circular(4)))))),
|
|
Positioned(bottom:40, left:24, right:24,
|
|
child:Row(children:[
|
|
if (_page>0)
|
|
TextButton(onPressed:()=>_ctrl.previousPage(
|
|
duration:const Duration(milliseconds:300),curve:Curves.easeOut),
|
|
child:const Text('Atras',style:TextStyle(color:AppColors.grisTexto)))
|
|
else
|
|
TextButton(onPressed:_finish,
|
|
child:const Text('Omitir',style:TextStyle(color:AppColors.grisTexto))),
|
|
const Spacer(),
|
|
ElevatedButton(
|
|
onPressed:_page<_pages.length-1
|
|
?()=>_ctrl.nextPage(duration:const Duration(milliseconds:300),curve:Curves.easeOut)
|
|
:_finish,
|
|
style:ElevatedButton.styleFrom(
|
|
backgroundColor:_pages[_page].color, foregroundColor:Colors.white,
|
|
padding:const EdgeInsets.symmetric(horizontal:28,vertical:12),
|
|
shape:RoundedRectangleBorder(borderRadius:BorderRadius.circular(25))),
|
|
child:Text(_page<_pages.length-1?'Siguiente':'Comenzar',
|
|
style:const TextStyle(fontWeight:FontWeight.bold))),
|
|
])),
|
|
]));
|
|
}
|
|
@override void dispose(){ _ctrl.dispose(); super.dispose(); }
|
|
}
|
|
|
|
class _OnboardPage {
|
|
final IconData icon; final Color color;
|
|
final String title, subtitle, desc;
|
|
const _OnboardPage({required this.icon,required this.color,
|
|
required this.title,required this.subtitle,required this.desc});
|
|
}
|
|
|
|
class _PageContent extends StatelessWidget {
|
|
final _OnboardPage page;
|
|
const _PageContent({super.key, required this.page});
|
|
@override
|
|
Widget build(BuildContext context) => Container(
|
|
decoration:BoxDecoration(gradient:LinearGradient(
|
|
begin:Alignment.topCenter, end:Alignment.bottomCenter,
|
|
colors:[page.color, page.color.withOpacity(0.85), Colors.white],
|
|
stops:const[0,0.4,0.7])),
|
|
child:SafeArea(child:Column(children:[
|
|
const SizedBox(height:48),
|
|
Container(width:120,height:120,
|
|
decoration:BoxDecoration(color:Colors.white.withOpacity(0.2),shape:BoxShape.circle,
|
|
border:Border.all(color:Colors.white.withOpacity(0.5),width:2)),
|
|
child:Icon(page.icon,size:60,color:Colors.white)),
|
|
const SizedBox(height:28),
|
|
Padding(padding:const EdgeInsets.symmetric(horizontal:32),child:Column(children:[
|
|
Text(page.title,textAlign:TextAlign.center,
|
|
style:const TextStyle(fontSize:24,fontWeight:FontWeight.bold,color:Colors.white)),
|
|
const SizedBox(height:10),
|
|
Text(page.subtitle,textAlign:TextAlign.center,
|
|
style:TextStyle(fontSize:14,color:Colors.white.withOpacity(0.9))),
|
|
])),
|
|
const SizedBox(height:32),
|
|
Expanded(child:Container(margin:const EdgeInsets.symmetric(horizontal:20),
|
|
padding:const EdgeInsets.all(22),
|
|
decoration:BoxDecoration(color:Colors.white,borderRadius:BorderRadius.circular(20),
|
|
boxShadow:[BoxShadow(color:page.color.withOpacity(0.2),blurRadius:20,offset:const Offset(0,8))]),
|
|
child:Text(page.desc,textAlign:TextAlign.left,
|
|
style:const TextStyle(fontSize:13,height:1.7,color:AppColors.negroTexto)))),
|
|
const SizedBox(height:110),
|
|
])));
|
|
}
|