diff --git a/.env b/.env index 2ff9338..57c6b36 100644 --- a/.env +++ b/.env @@ -1,3 +1 @@ -base_url=http://192.168.0.123:8080/ -URL_LOGIN=https://qacademico.ifce.edu.br/qacademico/index.asp?t=1001 -URL_SCHEDULE=https://qacademico.ifce.edu.br/qacademico/index.asp?t=2010 \ No newline at end of file +base_url=http://192.168.0.123:3000/ \ No newline at end of file diff --git a/README.md b/README.md index f342dbe..20444fa 100644 --- a/README.md +++ b/README.md @@ -1 +1,16 @@ # academico_mobile + + +Link do Figma: https://www.figma.com/file/tFLY9uOoiAXYoHrqj7uHiI/Untitled?node-id=0%3A1&t=LloEngcQkWGb6Bna-1 + +Atividade 01: + - [ ] Instalar Assets (Imagens e Fonts) + - [ ] ThemeData + - [ ] Estilos diferentes de Fonts + - [ ] Preparando Extensions de UI + - [ ] Dotenv + - [ ] RestClient - Dio + + - [ ] senha: cddbdabqeujfjluo + - [ ] lembrar do delay na requisição de 2s. +======= \ No newline at end of file diff --git a/assets/images/images_cards/report-card.png b/assets/images/images_cards/report-card.png new file mode 100644 index 0000000..2f0fc3f Binary files /dev/null and b/assets/images/images_cards/report-card.png differ diff --git a/assets/images/images_cards/schedule.png b/assets/images/images_cards/schedule.png new file mode 100644 index 0000000..9697a39 Binary files /dev/null and b/assets/images/images_cards/schedule.png differ diff --git a/assets/images/images_cards/timetable.png b/assets/images/images_cards/timetable.png new file mode 100644 index 0000000..1fad96e Binary files /dev/null and b/assets/images/images_cards/timetable.png differ diff --git a/assets/images/logos/logo_academico.png b/assets/images/logos/logo_academico.png new file mode 100644 index 0000000..a1dbdca Binary files /dev/null and b/assets/images/logos/logo_academico.png differ diff --git a/lib/academico_mobile.dart b/lib/academico_mobile.dart index 3cf409e..c481b34 100644 --- a/lib/academico_mobile.dart +++ b/lib/academico_mobile.dart @@ -2,7 +2,7 @@ import 'package:academico_mobile/app/core/provider/application_binding.dart'; import 'package:academico_mobile/app/core/ui/theme/theme_config.dart'; import 'package:academico_mobile/app/pages/daily/daily_router.dart'; import 'package:academico_mobile/app/pages/home/home_router.dart'; -import 'package:academico_mobile/app/pages/login/login_page.dart'; +import 'package:academico_mobile/app/pages/login/login_router.dart'; import 'package:academico_mobile/app/pages/recover_password/recover_password_page.dart'; import 'package:academico_mobile/app/pages/schedule/schedule_router.dart'; import 'package:academico_mobile/app/pages/splash/splash_page.dart'; @@ -20,7 +20,7 @@ class AcademicoMobile extends StatelessWidget { debugShowCheckedModeBanner: false, routes: { '/': (context) => const SplashPage(), - '/login': (context) => const LoginPage(), + '/login': (context) => LoginRouter.page, '/schedule': (context) => ScheduleRouter.page, '/daily': (context) => DailyRouter.page, '/home': (context) => HomeRouter.page, diff --git a/lib/app/core/exceptions/unauthorized_exception.dart b/lib/app/core/exceptions/unauthorized_exception.dart new file mode 100644 index 0000000..1ee5470 --- /dev/null +++ b/lib/app/core/exceptions/unauthorized_exception.dart @@ -0,0 +1 @@ +class UnauthorizedException implements Exception {} diff --git a/lib/app/core/provider/application_binding.dart b/lib/app/core/provider/application_binding.dart index d86bbee..6fc352d 100644 --- a/lib/app/core/provider/application_binding.dart +++ b/lib/app/core/provider/application_binding.dart @@ -1,4 +1,6 @@ import 'package:academico_mobile/app/core/rest_client/custom_dio.dart'; +import 'package:academico_mobile/app/repositories/auth/auth_repository.dart'; +import 'package:academico_mobile/app/repositories/auth/auth_repository_impl.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -16,7 +18,12 @@ class ApplicationBinding extends StatelessWidget { providers: [ Provider( create: (context) => CustomDio(), - ) + ), + Provider( + create: (context) => AuthRepositoryImpl( + dio: context.read(), + ), + ), ], child: child, ); diff --git a/lib/app/core/rest_client/custom_dio.dart b/lib/app/core/rest_client/custom_dio.dart index aa6332a..5890948 100644 --- a/lib/app/core/rest_client/custom_dio.dart +++ b/lib/app/core/rest_client/custom_dio.dart @@ -7,8 +7,8 @@ class CustomDio extends DioForNative { : super( BaseOptions( baseUrl: Env.instance['base_url'] ?? '', - connectTimeout: const Duration(milliseconds: 5000), - receiveTimeout: const Duration(milliseconds: 60000), + connectTimeout: const Duration(milliseconds: 10000), + receiveTimeout: const Duration(milliseconds: 180000), ), ) { interceptors.add(LogInterceptor( diff --git a/lib/app/core/ui/widgets/my_appbar.dart b/lib/app/core/ui/widgets/my_appbar.dart index 51cf578..d900ed4 100644 --- a/lib/app/core/ui/widgets/my_appbar.dart +++ b/lib/app/core/ui/widgets/my_appbar.dart @@ -15,7 +15,33 @@ class MyAppbar extends AppBar { required final VoidCallback onPressed, }) : super( elevation: elevation, - title: Text(title, style: TextStyles.instance.labelPage), + title: Text( + title, + style: TextStyles.instance.labelPage.copyWith( + fontSize: 15, + ), + ), + centerTitle: true, + backgroundColor: ColorsApp.instance.background, + leading: IconButton( + icon: Icon(icon), + onPressed: onPressed, + ), + ); + + MyAppbar.normal({ + super.key, + double elevation = 0, + required String title, + String subtitle = '', + IconData icon = Icons.arrow_back_ios, + required final VoidCallback onPressed, + }) : super( + elevation: elevation, + title: Text( + title, + style: TextStyles.instance.labelPage + ), centerTitle: true, backgroundColor: ColorsApp.instance.background, leading: IconButton( @@ -43,7 +69,7 @@ class MyAppbar extends AppBar { return Container( alignment: Alignment.center, padding: - const EdgeInsets.symmetric(vertical: 10, horizontal: 10), + const EdgeInsets.symmetric(vertical: 10, horizontal: 15), margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), decoration: BoxDecoration( @@ -58,7 +84,7 @@ class MyAppbar extends AppBar { color: state.isOn ? ColorsApp.instance.cardwhite : ColorsApp.instance.cardwhite, - fontSize: 15, + fontSize: 12, fontWeight: TextStyles.instance.textButtonLabel.fontWeight, ), diff --git a/lib/app/core/ui/widgets/my_input_button.dart b/lib/app/core/ui/widgets/my_input_button.dart index 09f2b4f..ce8330f 100644 --- a/lib/app/core/ui/widgets/my_input_button.dart +++ b/lib/app/core/ui/widgets/my_input_button.dart @@ -1,4 +1,5 @@ import 'package:academico_mobile/app/core/ui/styles/app_styles.dart'; +import 'package:academico_mobile/app/core/ui/styles/colors_app.dart'; import 'package:flutter/material.dart'; class MyInputButton extends StatelessWidget { @@ -21,7 +22,11 @@ class MyInputButton extends StatelessWidget { width: width, height: height, child: ElevatedButton( - style: context.appStyles.buttonStyle, + style: context.appStyles.buttonStyle.copyWith( + backgroundColor: MaterialStateProperty.all( + ColorsApp.instance.cardblue, + ), + ), onPressed: onPressed, child: Text(label), ), diff --git a/lib/app/models/auth_model.dart b/lib/app/models/auth_model.dart new file mode 100644 index 0000000..f9fc34a --- /dev/null +++ b/lib/app/models/auth_model.dart @@ -0,0 +1,25 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:convert'; + +class AuthModel { + final String accessToken; + + AuthModel({required this.accessToken}); + + Map toMap() { + return { + 'access_token': accessToken, + }; + } + + factory AuthModel.fromMap(Map map) { + return AuthModel( + accessToken: map['access_token'] ?? '', + ); + } + + String toJson() => json.encode(toMap()); + + factory AuthModel.fromJson(String source) => + AuthModel.fromMap(json.decode(source) as Map); +} diff --git a/lib/app/models/daily_model.dart b/lib/app/models/daily_model.dart index 36a5ceb..692782b 100644 --- a/lib/app/models/daily_model.dart +++ b/lib/app/models/daily_model.dart @@ -19,7 +19,7 @@ class SemestreModel { } class DisciplinaModel { - int id; + String id; String nome; String professor; Resumo resumo; @@ -37,8 +37,8 @@ class DisciplinaModel { List avaliacoes = List.from(json["avaliacoes"].map((x) => x.toString())); return DisciplinaModel( - id: json["id"], - nome: json["nome"], + id: json["id"] ?? '0', + nome: json["nome"] ?? '', professor: json["professor"], resumo: Resumo.fromJson(json["resumo"]), avaliacoes: avaliacoes); @@ -56,7 +56,7 @@ class DisciplinaModel { class Resumo { String cargaHoraria; String faltas; - List aulasFuturas; + String aulasFuturas; List presencas; List ausencias; List pendentes; @@ -80,13 +80,10 @@ class Resumo { List pendentes = List.from( json["pendentes"].map((x) => x.toString()), ); - List aulasFuturas = List.from( - json["aulas_futuras"].map((x) => x.toString()), - ); return Resumo( cargaHoraria: json["carga_horaria"], faltas: json["faltas"], - aulasFuturas: aulasFuturas, + aulasFuturas: json["aulas_futuras"], presencas: presencas, ausencias: ausencias, pendentes: pendentes); @@ -95,7 +92,7 @@ class Resumo { Map toJson() => { "carga_horaria": cargaHoraria, "faltas": faltas, - "aulas_futuras": List.from(aulasFuturas.map((x) => x)), + "aulas_futuras": aulasFuturas, "presencas": List.from(presencas.map((x) => x)), "ausencias": List.from(ausencias.map((x) => x)), "pendentes": List.from(pendentes.map((x) => x)), diff --git a/lib/app/pages/daily/daily_controller.dart b/lib/app/pages/daily/daily_controller.dart index cde85ae..c30ac86 100644 --- a/lib/app/pages/daily/daily_controller.dart +++ b/lib/app/pages/daily/daily_controller.dart @@ -15,15 +15,18 @@ class DailyController extends Cubit { Future loadSemestre() async { emit(state.copyWith(status: DailyStateSatus.loading)); try { - await Future.delayed(const Duration(seconds: 2)); final semestres = await _dailyRepository.findDaily(); emit( - state.copyWith(status: DailyStateSatus.loaded, semestres: semestres)); + state.copyWith(status: DailyStateSatus.loaded, semestres: semestres), + ); } catch (e, s) { log('Erro ao buscar semestres', error: e, stackTrace: s); - emit(state.copyWith( + emit( + state.copyWith( status: DailyStateSatus.error, - errorMessage: 'Erro ao buscar semestres')); + errorMessage: 'Erro ao buscar semestres', + ), + ); } } @@ -32,7 +35,13 @@ class DailyController extends Cubit { await Future.delayed( const Duration(seconds: 2), ); - emit(state.copyWith(status: DailyStateSatus.loaded, isNow: !state.isNow)); + emit( + state.copyWith( + status: DailyStateSatus.loaded, + isNow: !state.isNow, + + ), + ); } Future selectedDay(int index) async { @@ -40,4 +49,5 @@ class DailyController extends Cubit { await Future.delayed(const Duration(seconds: 1)); emit(state.copyWith(status: DailyStateSatus.loaded, selected: index)); } + } diff --git a/lib/app/pages/daily/daily_page.dart b/lib/app/pages/daily/daily_page.dart index c595b83..2907298 100644 --- a/lib/app/pages/daily/daily_page.dart +++ b/lib/app/pages/daily/daily_page.dart @@ -20,19 +20,19 @@ class DailyPage extends StatefulWidget { } class _DailyPageState extends BaseState { + late List list = []; @override - void onReady() { - super.onReady(); - controller.loadSemestre(); - list = controller.state.semestres[0].disciplinas; + void onReady() async { + await controller.loadSemestre(); + if (controller.state.isNow) { + list = controller.state.semestres[0].disciplinas; + } } - List list = []; - @override Widget build(BuildContext context) { return Scaffold( - appBar: MyAppbar( + appBar: MyAppbar.normal( title: 'Meus Diários', onPressed: () => Navigator.pop(context), ), @@ -79,7 +79,12 @@ class _DailyPageState extends BaseState { borderRadius: BorderRadius.circular(5), ), child: TextButton( - onPressed: () => controller.selectedDay(index), + onPressed: () { + setState(() { + controller.selectedDay(index); + list = state.semestres[index].disciplinas; + }); + }, child: Text( state.semestres[index].semestre, style: TextStyles.instance.texLabelH2.copyWith( @@ -102,20 +107,41 @@ class _DailyPageState extends BaseState { padding: EdgeInsets.only(left: context.percentWidth(0.03)), child: const LabelSubtitle(title: 'Disciplinas'), ), - BlocListener( + BlocConsumer( listener: (context, state) { - list = state.semestres[state.selected].disciplinas; - }, - child: Expanded( - child: ListView.builder( - itemCount: list.length, - itemBuilder: (context, index) { - return ListaCardDisciplina( - disciplina: list[index], - ); + state.status.matchAny( + any: () => hideLoader(), + loading: () => showLoader(), + error: () { + hideLoader(); + showError(state.errorMessage ?? + 'Erro ao carregar o cronograma'); }, - ), + ); + }, + buildWhen: (previous, current) => current.status.matchAny( + any: () => false, + initial: () => true, + loaded: () => true, ), + builder: (context, state) { + if (state.isNow) { + list = state.semestres[0].disciplinas; + } else { + list = state.semestres[state.selected].disciplinas; + } + return Expanded( + child: ListView.builder( + shrinkWrap: true, + itemCount: list.length, + itemBuilder: (BuildContext context, int index) { + return ListaCardDisciplina( + disciplina: list[index], + ); + }, + ), + ); + }, ), SizedBox(height: context.percentHeight(0.02)) ], diff --git a/lib/app/pages/daily/daily_state.dart b/lib/app/pages/daily/daily_state.dart index 7c4f14f..3844ea1 100644 --- a/lib/app/pages/daily/daily_state.dart +++ b/lib/app/pages/daily/daily_state.dart @@ -22,6 +22,7 @@ class DailyState extends Equatable { final String? errorMessage; final bool isNow; final int selected; + const DailyState({ required this.status, @@ -33,7 +34,7 @@ class DailyState extends Equatable { DailyState.initial() : status = DailyStateSatus.initial, - semestres = [], + semestres = [], errorMessage = null, isNow = true, selected = 0; diff --git a/lib/app/pages/daily/daily_state.g.dart b/lib/app/pages/daily/daily_state.g.dart index bd2259f..58a637a 100644 --- a/lib/app/pages/daily/daily_state.g.dart +++ b/lib/app/pages/daily/daily_state.g.dart @@ -11,6 +11,8 @@ extension DailyStateSatusMatch on DailyStateSatus { {required T Function() initial, required T Function() loading, required T Function() loaded, + required T Function() isNow, + required T Function() selected, required T Function() error}) { final v = this; if (v == DailyStateSatus.initial) { @@ -25,6 +27,14 @@ extension DailyStateSatusMatch on DailyStateSatus { return loaded(); } + if (v == DailyStateSatus.isNow) { + return isNow(); + } + + if (v == DailyStateSatus.selected) { + return selected(); + } + if (v == DailyStateSatus.error) { return error(); } @@ -37,6 +47,8 @@ extension DailyStateSatusMatch on DailyStateSatus { T Function()? initial, T Function()? loading, T Function()? loaded, + T Function()? isNow, + T Function()? selected, T Function()? error}) { final v = this; if (v == DailyStateSatus.initial && initial != null) { @@ -51,6 +63,14 @@ extension DailyStateSatusMatch on DailyStateSatus { return loaded(); } + if (v == DailyStateSatus.isNow && isNow != null) { + return isNow(); + } + + if (v == DailyStateSatus.selected && selected != null) { + return selected(); + } + if (v == DailyStateSatus.error && error != null) { return error(); } diff --git a/lib/app/pages/daily/widgets/lista_card_disciplina.dart b/lib/app/pages/daily/widgets/lista_card_disciplina.dart index 3a66116..042bc83 100644 --- a/lib/app/pages/daily/widgets/lista_card_disciplina.dart +++ b/lib/app/pages/daily/widgets/lista_card_disciplina.dart @@ -19,13 +19,15 @@ class ListaCardDisciplina extends StatelessWidget { Widget build(BuildContext context) { return InkWell( splashColor: ColorsApp.instance.background, - onTap: () => Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => DashboardPage( - disciplina: disciplina, + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => DashboardPage( + disciplina: disciplina, + ), ), - ), - ), + ); + }, child: Container( padding: const EdgeInsets.all(10), margin: const EdgeInsets.only(bottom: 15, left: 10, right: 10, top: 0), @@ -42,31 +44,38 @@ class ListaCardDisciplina extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - disciplina.id.toString(), - style: TextStyles.instance.texLabelH5.copyWith( - color: ColorsApp.instance.labelblack1, - fontSize: 14, - fontWeight: - TextStyles.instance.textExtraBold.fontWeight, + Container( + padding: EdgeInsets.all(context.screenHeight * 0.005), + decoration: BoxDecoration( + color: ColorsApp.instance.cardblue, + borderRadius: BorderRadius.circular(5), + ), + child: Text( + disciplina.id.trim(), + style: TextStyles.instance.texLabelH4.copyWith( + color: ColorsApp.instance.cardwhite, + fontWeight: TextStyles.instance.textBold.fontWeight, + fontSize: context.screenHeight * 0.017, + ), ), ), - SizedBox(height: context.screenHeight * 0.005), + SizedBox(height: context.screenHeight * 0.007), Text( - disciplina.nome, - style: TextStyles.instance.texLabelH4.copyWith( + disciplina.nome.trim(), + style: TextStyles.instance.texLabelH4.copyWith( color: ColorsApp.instance.labelblack1, - fontSize: 17, + fontSize: 15, fontWeight: - TextStyles.instance.textSemiBold.fontWeight), + TextStyles.instance.textBold.fontWeight), ), - SizedBox(height: context.screenHeight * 0.002), + SizedBox(height: context.screenHeight * 0.007), Text( - disciplina.professor, + disciplina.professor.trim(), style: TextStyles.instance.texLabelH5.copyWith( - color: ColorsApp.instance.labelblack1, - fontWeight: - TextStyles.instance.textSemiBold.fontWeight), + color: ColorsApp.instance.labelblack1, + fontWeight: + TextStyles.instance.textSemiBold.fontWeight, + ), ), ], ), diff --git a/lib/app/pages/daily/widgets/my_switch.dart b/lib/app/pages/daily/widgets/my_switch.dart index b5df70d..faa0fb0 100644 --- a/lib/app/pages/daily/widgets/my_switch.dart +++ b/lib/app/pages/daily/widgets/my_switch.dart @@ -7,7 +7,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class MySwitch extends StatefulWidget { - const MySwitch({super.key}); + const MySwitch({ + super.key, + }); @override State createState() => _MySwitchState(); diff --git a/lib/app/pages/dashbord_daily/dashboard_page.dart b/lib/app/pages/dashbord_daily/dashboard_page.dart index 1e316ba..892a810 100644 --- a/lib/app/pages/dashbord_daily/dashboard_page.dart +++ b/lib/app/pages/dashbord_daily/dashboard_page.dart @@ -68,18 +68,17 @@ class _DashboardPageState extends State { label: 'Carga Horária Total', value: '${widget.disciplina.resumo.cargaHoraria}H', ), - Row( - children: [ - Text( - 'Existem 6 aulas planejadas a mais que as necessárias.', - style: TextStyles.instance.texLabelH5.copyWith( - color: ColorsApp.instance.cardgrey, - fontWeight: TextStyles.instance.textSemiBold.fontWeight, - ), - ), - ], - ), - SizedBox(height: context.screenHeight * 0.01), + // Row( + // children: [ + // Text( + // 'Existem 6 aulas planejadas a mais que as necessárias.', + // style: TextStyles.instance.texLabelH5.copyWith( + // color: ColorsApp.instance.cardgrey, + // fontWeight: TextStyles.instance.textSemiBold.fontWeight, + // ), + // ), + // ], + // ), CardHorario( label: 'Você ainda pode ter', value: '${widget.disciplina.resumo.faltas} falta(s)', @@ -87,8 +86,7 @@ class _DashboardPageState extends State { ), CardFaltasFuturas( label: 'Horas aula futuras', - value: '${widget.disciplina.resumo.aulasFuturas[0]}H', - percent: '${widget.disciplina.resumo.aulasFuturas[1]}%', + value: '${widget.disciplina.resumo.aulasFuturas}H', ), SizedBox(height: context.screenHeight * 0.02), Row( @@ -104,7 +102,7 @@ class _DashboardPageState extends State { ], ), Divider( - color: ColorsApp.instance.cardnoselected, + color: ColorsApp.instance.background, thickness: 1, ), LineDashboard( @@ -136,13 +134,34 @@ class _DashboardPageState extends State { ], ), Divider( - color: ColorsApp.instance.cardnoselected, + color: ColorsApp.instance.background, thickness: 1, ), - LineNotas(label: 'N1', value: 7.0.toString()), - LineNotas(label: 'N2', value: 7.0.toString()), - LineNotas(label: 'Media Parcial', value: 7.0.toString()), - LineNotas(label: 'Prova Final', value: 7.0.toString()), + LineNotas( + label: 'N1', + value: widget.disciplina.avaliacoes[0].toString() == '' + ? ' ' + : widget.disciplina.avaliacoes[0].toString()), + LineNotas( + label: 'N2', + value: widget.disciplina.avaliacoes[1].toString() == '' + ? ' ' + : widget.disciplina.avaliacoes[1].toString()), + LineNotas( + label: 'Média Parcial', + value: widget.disciplina.avaliacoes[2].toString() == '' + ? ' ' + : widget.disciplina.avaliacoes[2].toString()), + LineNotas( + label: 'Prova Final', + value: widget.disciplina.avaliacoes[3].toString() == '' + ? ' ' + : widget.disciplina.avaliacoes[3].toString()), + LineNotas( + label: 'Média Final', + value: widget.disciplina.avaliacoes[4].toString() == '' + ? ' ' + : widget.disciplina.avaliacoes[4].toString()), ], ), ), diff --git a/lib/app/pages/dashbord_daily/widgets/card_aulas_futuras.dart b/lib/app/pages/dashbord_daily/widgets/card_aulas_futuras.dart index a3fa35d..9a61cc5 100644 --- a/lib/app/pages/dashbord_daily/widgets/card_aulas_futuras.dart +++ b/lib/app/pages/dashbord_daily/widgets/card_aulas_futuras.dart @@ -6,13 +6,11 @@ import 'package:flutter/material.dart'; class CardFaltasFuturas extends StatelessWidget { final String label; final String value; - final String percent; const CardFaltasFuturas({ super.key, required this.label, required this.value, - required this.percent, }); @override @@ -43,16 +41,6 @@ class CardFaltasFuturas extends StatelessWidget { ), ), ), - Padding( - padding: const EdgeInsets.all(10), - child: Text( - percent, - style: TextStyles.instance.texLabelH3.copyWith( - color: ColorsApp.instance.cardwhite, - fontWeight: TextStyles.instance.textSemiBold.fontWeight, - ), - ), - ), ], ), ); diff --git a/lib/app/pages/dashbord_daily/widgets/line_dashboard.dart b/lib/app/pages/dashbord_daily/widgets/line_dashboard.dart index b0571cd..1033539 100644 --- a/lib/app/pages/dashbord_daily/widgets/line_dashboard.dart +++ b/lib/app/pages/dashbord_daily/widgets/line_dashboard.dart @@ -29,7 +29,7 @@ class LineDashboard extends StatelessWidget { child: Text( label, style: TextStyles.instance.texLabelH4.copyWith( - color: ColorsApp.instance.labelblack4, + color: ColorsApp.instance.cardwhite, fontWeight: TextStyles.instance.textSemiBold.fontWeight, ), ), @@ -39,7 +39,7 @@ class LineDashboard extends StatelessWidget { child: Text( value, style: TextStyles.instance.texLabelH4.copyWith( - color: ColorsApp.instance.labelblack4, + color: ColorsApp.instance.cardwhite, fontWeight: TextStyles.instance.textSemiBold.fontWeight, ), ), @@ -49,7 +49,7 @@ class LineDashboard extends StatelessWidget { child: Text( '$percent%', style: TextStyles.instance.texLabelH4.copyWith( - color: ColorsApp.instance.labelblack4, + color: ColorsApp.instance.cardwhite, fontWeight: TextStyles.instance.textSemiBold.fontWeight, ), ), diff --git a/lib/app/pages/dashbord_daily/widgets/line_notas.dart b/lib/app/pages/dashbord_daily/widgets/line_notas.dart index f9d2b0f..c563f48 100644 --- a/lib/app/pages/dashbord_daily/widgets/line_notas.dart +++ b/lib/app/pages/dashbord_daily/widgets/line_notas.dart @@ -24,7 +24,7 @@ class LineNotas extends StatelessWidget { child: Text( label, style: TextStyles.instance.texLabelH4.copyWith( - color: ColorsApp.instance.labelblack4, + color: ColorsApp.instance.cardwhite, fontWeight: TextStyles.instance.textSemiBold.fontWeight, ), ), @@ -34,7 +34,7 @@ class LineNotas extends StatelessWidget { child: Text( value, style: TextStyles.instance.texLabelH4.copyWith( - color: ColorsApp.instance.labelblack4, + color: ColorsApp.instance.cardwhite, fontWeight: TextStyles.instance.textSemiBold.fontWeight, ), ), diff --git a/lib/app/pages/home/home_controller.dart b/lib/app/pages/home/home_controller.dart index d2056b9..f1f6714 100644 --- a/lib/app/pages/home/home_controller.dart +++ b/lib/app/pages/home/home_controller.dart @@ -24,4 +24,5 @@ class HomeController extends Cubit { errorMessage: 'Error ao carregar home page')); } } + } diff --git a/lib/app/pages/home/widgets/card_home.dart b/lib/app/pages/home/widgets/card_home.dart index 2c12bef..8f18b5e 100644 --- a/lib/app/pages/home/widgets/card_home.dart +++ b/lib/app/pages/home/widgets/card_home.dart @@ -1,9 +1,8 @@ +import 'package:academico_mobile/app/core/ui/helpers/size_extensions.dart'; import 'package:academico_mobile/app/core/ui/styles/colors_app.dart'; import 'package:academico_mobile/app/core/ui/styles/text_styles.dart'; import 'package:flutter/material.dart'; - import 'package:academico_mobile/app/models/home_model.dart'; -import 'package:loading_gifs/loading_gifs.dart'; class CardHome extends StatelessWidget { final HomePageModel listaCards; @@ -15,43 +14,44 @@ class CardHome extends StatelessWidget { @override Widget build(BuildContext context) { - return ElevatedButton( - style: ButtonStyle( - backgroundColor: - MaterialStateProperty.all(ColorsApp.instance.cardwhite), - shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(5), + return Padding( + padding: const EdgeInsets.only(bottom: 10), + child: ElevatedButton( + style: ButtonStyle( + backgroundColor: + MaterialStateProperty.all(ColorsApp.instance.primary), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5), + ), ), ), - ), - onPressed: () => Navigator.pushNamed(context, listaCards.url), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.all(5.0), - child: - FadeInImage.assetNetwork( - placeholder: cupertinoActivityIndicator, - image: listaCards.image, - height: 100, - width: 110, + onPressed: () => Navigator.pushNamed(context, listaCards.url), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset( + listaCards.image, + height: context.percentWidth(.15), fit: BoxFit.cover, ), - ), - Text( - listaCards.name, - style: TextStyles.instance.texLabelH4.copyWith( - color: ColorsApp.instance.background, - fontWeight: TextStyles.instance.textSemiBold.fontWeight, + SizedBox( + height: context.screenWidth * 0.035, ), - ), - ], + Text( + listaCards.name, + style: TextStyles.instance.texLabelH4.copyWith( + fontSize: context.screenWidth * 0.045, + color: ColorsApp.instance.cardwhite, + fontWeight: TextStyles.instance.textSemiBold.fontWeight, + ), + ), + ], + ), ), ), ); diff --git a/lib/app/pages/login/login_controller.dart b/lib/app/pages/login/login_controller.dart new file mode 100644 index 0000000..71e0cd6 --- /dev/null +++ b/lib/app/pages/login/login_controller.dart @@ -0,0 +1,36 @@ +import 'dart:developer'; +import 'package:academico_mobile/app/core/exceptions/unauthorized_exception.dart'; +import 'package:academico_mobile/app/pages/login/login_state.dart'; +import 'package:academico_mobile/app/repositories/auth/auth_repository.dart'; +import 'package:bloc/bloc.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class LoginController extends Cubit { + final AuthRepository _authRepository; + + LoginController(this._authRepository) : super(const LoginState.initial()); + + Future login(String matricula, String password) async { + try { + emit(state.copyWith(status: LoginStatus.login)); + final authModel = await _authRepository.login(matricula, password); + final prefs = await SharedPreferences.getInstance(); + await prefs.setString('access_token', authModel.accessToken); + emit(state.copyWith(status: LoginStatus.sucess)); + } on UnauthorizedException catch (e, s) { + log('Permissão Negada Controller 1', error: e, stackTrace: s); + emit( + state.copyWith( + status: LoginStatus.loginError, + errorMessage: 'Permissão Negada Controller 2'), + ); + } catch (e, s) { + log('Erro ao realizar Login Controller 1', error: e, stackTrace: s); + emit( + state.copyWith( + status: LoginStatus.error, + errorMessage: 'Erro ao realizar Login Controller 2'), + ); + } + } +} diff --git a/lib/app/pages/login/login_page.dart b/lib/app/pages/login/login_page.dart index 7783ca1..60ff3c2 100644 --- a/lib/app/pages/login/login_page.dart +++ b/lib/app/pages/login/login_page.dart @@ -1,11 +1,15 @@ // ignore_for_file: unnecessary_string_interpolations, avoid_print +import 'package:academico_mobile/app/core/ui/base_state/base_state.dart'; import 'package:academico_mobile/app/core/ui/helpers/size_extensions.dart'; import 'package:academico_mobile/app/core/ui/styles/colors_app.dart'; import 'package:academico_mobile/app/core/ui/styles/text_styles.dart'; import 'package:academico_mobile/app/core/ui/widgets/my_input_button.dart'; +import 'package:academico_mobile/app/pages/login/login_controller.dart'; +import 'package:academico_mobile/app/pages/login/login_state.dart'; import 'package:academico_mobile/app/pages/login/widgets/my_sliverfillremaining.dart'; import 'package:flutter/material.dart'; -import 'package:webview_flutter/webview_flutter.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:validatorless/validatorless.dart'; class LoginPage extends StatefulWidget { const LoginPage({super.key}); @@ -14,136 +18,153 @@ class LoginPage extends StatefulWidget { State createState() => _LoginPageState(); } -class _LoginPageState extends State { +class _LoginPageState extends BaseState { final formKey = GlobalKey(); TextEditingController matriculaEC = TextEditingController(); - TextEditingController senhaEC = TextEditingController(); + TextEditingController passwordEC = TextEditingController(); bool _obscuredText = true; - late WebViewController controller; - @override - void initState() { - controller = WebViewController() - ..setJavaScriptMode(JavaScriptMode.unrestricted) - ..setBackgroundColor(const Color(0x00000000)) - ..loadRequest(Uri.parse( - 'https://qacademico.ifce.edu.br/qacademico/index.asp?t=1001')); - super.initState(); - } @override void dispose() { matriculaEC.dispose(); - senhaEC.dispose(); + passwordEC.dispose(); super.dispose(); } @override Widget build(BuildContext context) { - return Scaffold( - body: CustomScrollView( - slivers: [ - SliverToBoxAdapter( - child: Padding( - padding: EdgeInsets.all(context.screenWidth * .03), - child: Form( - key: formKey, - child: Column( - children: [ - SizedBox( - height: context.percentHeight(.15), - ), - Image.asset( - 'assets/images/logos/logo_ifce.png', - height: context.percentHeight(.2), - fit: BoxFit.cover, - ), - SizedBox( - height: context.percentHeight(.05), - ), - TextFormField( - controller: matriculaEC, - keyboardType: TextInputType.number, - style: TextStyles.instance.texLabelH4.copyWith( - color: ColorsApp.instance.labelblack1, - fontSize: 20, - fontWeight: - TextStyles.instance.textButtonLabel.fontWeight, - ), - decoration: const InputDecoration( - hintText: 'matricula', - ), - ), - const SizedBox( - height: 20, - ), - TextFormField( - controller: senhaEC, - obscureText: _obscuredText, - style: TextStyles.instance.texLabelH4.copyWith( - color: ColorsApp.instance.labelblack1, - fontSize: 20, - fontWeight: - TextStyles.instance.textButtonLabel.fontWeight, - ), - decoration: InputDecoration( - hintText: 'senha', - suffixIcon: IconButton( - onPressed: () { - setState(() { - _obscuredText = !_obscuredText; - }); - }, - icon: Icon( - _obscuredText - ? Icons.visibility - : Icons.visibility_off, - color: ColorsApp.instance.labelblack1, - size: 25), + return BlocListener( + listener: (context, state) { + state.status.matchAny( + any: () => hideLoader(), + login: () => showLoader(), + loginError: () { + hideLoader(); + showError('Login ou Senha inválidos'); + }, + error: () { + hideLoader(); + showError('Error ao realizar login'); + }, + sucess: () { + hideLoader(); + Navigator.of(context).pushReplacementNamed('/home'); + }, + ); + }, + child: GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Scaffold( + body: CustomScrollView( + slivers: [ + SliverToBoxAdapter( + child: Padding( + padding: EdgeInsets.all(context.screenWidth * .03), + child: Form( + key: formKey, + child: Column( + children: [ + SizedBox( + height: context.percentHeight(.15), ), - ), - ), - const SizedBox( - height: 30, - ), - Center( - child: Row( - children: [ - Expanded( - child: MyInputButton( - height: context.screenHeight * .07, - label: 'Acessar', + Image.asset( + 'assets/images/logos/logo_ifce.png', + height: context.percentHeight(.2), + fit: BoxFit.cover, + ), + SizedBox( + height: context.percentHeight(.05), + ), + TextFormField( + controller: matriculaEC, + validator: + Validatorless.required('Matricula Obrigatória'), + keyboardType: TextInputType.number, + style: TextStyles.instance.texLabelH4.copyWith( + color: ColorsApp.instance.labelblack1, + fontSize: 20, + fontWeight: + TextStyles.instance.textButtonLabel.fontWeight, + ), + decoration: const InputDecoration( + hintText: 'matricula', + ), + ), + const SizedBox( + height: 20, + ), + TextFormField( + controller: passwordEC, + validator: Validatorless.multiple([ + Validatorless.required('Senha Obrigatória'), + Validatorless.min( + 6, + 'Senha deve ter no mínimo 6 caracteres', + ) + ]), + obscureText: _obscuredText, + style: TextStyles.instance.texLabelH4.copyWith( + color: ColorsApp.instance.labelblack1, + fontSize: 20, + fontWeight: + TextStyles.instance.textButtonLabel.fontWeight, + ), + decoration: InputDecoration( + hintText: 'senha', + suffixIcon: IconButton( onPressed: () { - print('${matriculaEC.text}'); - print('${senhaEC.text}'); - Navigator.of(context) - .pushReplacementNamed('/home'); + setState(() { + _obscuredText = !_obscuredText; + }); }, + icon: Icon( + _obscuredText + ? Icons.visibility + : Icons.visibility_off, + color: ColorsApp.instance.labelblack1, + size: 25), ), ), - ], - ), - ), - SizedBox( - height: context.screenHeight * .07, + ), + const SizedBox( + height: 30, + ), + Center( + child: Row( + children: [ + Expanded( + child: MyInputButton( + height: context.screenHeight * .07, + label: 'Acessar', + onPressed: () async { + final formValid = + formKey.currentState?.validate() ?? + false; + if (formValid) { + await controller.login( + matriculaEC.text, + passwordEC.text, + ); + } + }, + ), + ), + ], + ), + ), + SizedBox( + height: context.screenHeight * .07, + ), + ], ), - ], + ), ), ), - ), + const MySliverfillremaining() + ], ), - const MySliverfillremaining() - ], + ), ), ); } } - - -// controller -// ..runJavaScript( -// "javascript:document.getElementById('txtLogin').value = '${matriculaEC.text}'") -// ..runJavaScript( -// "javascript:document.getElementById('txtSenha').value = '${senhaEC.text}'") -// ..runJavaScript( -// "javascript:document.forms['frmLogin'].submit()"); -// print('foi'); \ No newline at end of file diff --git a/lib/app/pages/login/login_router.dart b/lib/app/pages/login/login_router.dart index 669b95a..8df745d 100644 --- a/lib/app/pages/login/login_router.dart +++ b/lib/app/pages/login/login_router.dart @@ -1,12 +1,18 @@ -// import 'package:academico_mobile/app/pages/login/login_page.dart'; -// import 'package:flutter/material.dart'; -// import 'package:provider/provider.dart'; +import 'package:academico_mobile/app/pages/login/login_controller.dart'; +import 'package:academico_mobile/app/pages/login/login_page.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; -// class LoginRouter { -// LoginRouter._(); -// static Widget get page => MultiProvider( -// providers: [ -// ], -// child: const LoginPage(), -// ); -// } +class LoginRouter { + LoginRouter._(); + static Widget get page => MultiProvider( + providers: [ + Provider( + create: (context) => LoginController( + context.read(), + ), + ), + ], + child: const LoginPage(), + ); +} diff --git a/lib/app/pages/login/login_state.dart b/lib/app/pages/login/login_state.dart new file mode 100644 index 0000000..3d01d5b --- /dev/null +++ b/lib/app/pages/login/login_state.dart @@ -0,0 +1,41 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'package:equatable/equatable.dart'; +import 'package:match/match.dart'; + +part 'login_state.g.dart'; + +@match +enum LoginStatus { + initial, + login, + sucess, + loginError, + error, +} + +class LoginState extends Equatable { + final LoginStatus status; + final String? errorMessage; + + const LoginState({ + required this.status, + this.errorMessage, + }); + + const LoginState.initial() + : status = LoginStatus.initial, + errorMessage = null; + + @override + List get props => [status, errorMessage]; + + LoginState copyWith({ + LoginStatus? status, + String? errorMessage, + }) { + return LoginState( + status: status ?? this.status, + errorMessage: errorMessage ?? this.errorMessage, + ); + } +} diff --git a/lib/app/pages/login/login_state.g.dart b/lib/app/pages/login/login_state.g.dart new file mode 100644 index 0000000..9d7bcb3 --- /dev/null +++ b/lib/app/pages/login/login_state.g.dart @@ -0,0 +1,70 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'login_state.dart'; + +// ************************************************************************** +// MatchExtensionGenerator +// ************************************************************************** + +extension LoginStatusMatch on LoginStatus { + T match( + {required T Function() initial, + required T Function() login, + required T Function() sucess, + required T Function() loginError, + required T Function() error}) { + final v = this; + if (v == LoginStatus.initial) { + return initial(); + } + + if (v == LoginStatus.login) { + return login(); + } + + if (v == LoginStatus.sucess) { + return sucess(); + } + + if (v == LoginStatus.loginError) { + return loginError(); + } + + if (v == LoginStatus.error) { + return error(); + } + + throw Exception('LoginStatus.match failed, found no match for: $this'); + } + + T matchAny( + {required T Function() any, + T Function()? initial, + T Function()? login, + T Function()? sucess, + T Function()? loginError, + T Function()? error}) { + final v = this; + if (v == LoginStatus.initial && initial != null) { + return initial(); + } + + if (v == LoginStatus.login && login != null) { + return login(); + } + + if (v == LoginStatus.sucess && sucess != null) { + return sucess(); + } + + if (v == LoginStatus.loginError && loginError != null) { + return loginError(); + } + + if (v == LoginStatus.error && error != null) { + return error(); + } + + return any(); + } +} diff --git a/lib/app/pages/schedule/schedule_page.dart b/lib/app/pages/schedule/schedule_page.dart index d3174cb..b40a110 100644 --- a/lib/app/pages/schedule/schedule_page.dart +++ b/lib/app/pages/schedule/schedule_page.dart @@ -20,8 +20,8 @@ class SchedulePage extends StatefulWidget { class _SchedulePageState extends BaseState { @override - void onReady() { - controller.loadSchedule(); + void onReady() async { + await controller.loadSchedule(); } List list = []; @@ -29,7 +29,7 @@ class _SchedulePageState extends BaseState { @override Widget build(BuildContext context) { return Scaffold( - appBar: MyAppbar( + appBar: MyAppbar.normal( title: 'Cronograma de Aulas', onPressed: () => Navigator.pop(context), ), @@ -63,23 +63,34 @@ class _SchedulePageState extends BaseState { ), SizedBox(height: context.percentWidth(0.05)), SizedBox( - height: 80, + height: 90, child: ListView.builder( scrollDirection: Axis.horizontal, itemCount: state.schedule.length, itemBuilder: (context, index) { - list = state.schedule[state.selectedDay!].horarios; + final now = DateTime.now(); + final startOfWeek = + now.subtract(Duration(days: now.weekday)); + final List daysOfWeek = []; + for (int i = 0; i < 7; i++) { + daysOfWeek.add(startOfWeek.add(Duration(days: i))); + } + list = state + .schedule[state.selectedDay ?? DateTime.now().weekday] + .horarios; return Padding( padding: const EdgeInsets.only(right: 10), child: LineDays( - day: state.schedule[index], - color: state.selectedDay == index - ? ColorsApp.instance.cardwhite - : ColorsApp.instance.cardnoselected, - onPressed: () async { - await controller.selectedDay(index); - list = state.schedule[index].horarios; - }), + day: state.schedule[index], + hoje: daysOfWeek[index].day, + color: state.selectedDay == index + ? ColorsApp.instance.cardwhite + : ColorsApp.instance.cardnoselected, + onPressed: () async { + await controller.selectedDay(index); + list = state.schedule[index].horarios; + }, + ), ); }, ), diff --git a/lib/app/pages/schedule/schedule_state.dart b/lib/app/pages/schedule/schedule_state.dart index 2787ff2..41569a4 100644 --- a/lib/app/pages/schedule/schedule_state.dart +++ b/lib/app/pages/schedule/schedule_state.dart @@ -32,7 +32,7 @@ class ScheduleState extends Equatable { : status = ScheduleStatus.initial, schedule = [], errorMessage = null, - selectedDay = 1; + selectedDay = DateTime.now().weekday; @override List get props => [status, schedule, errorMessage, selectedDay]; diff --git a/lib/app/pages/schedule/schedule_state.g.dart b/lib/app/pages/schedule/schedule_state.g.dart index 862bed0..745072f 100644 --- a/lib/app/pages/schedule/schedule_state.g.dart +++ b/lib/app/pages/schedule/schedule_state.g.dart @@ -11,6 +11,7 @@ extension ScheduleStatusMatch on ScheduleStatus { {required T Function() initial, required T Function() loading, required T Function() loaded, + required T Function() selectedDay, required T Function() error}) { final v = this; if (v == ScheduleStatus.initial) { @@ -25,6 +26,10 @@ extension ScheduleStatusMatch on ScheduleStatus { return loaded(); } + if (v == ScheduleStatus.selectedDay) { + return selectedDay(); + } + if (v == ScheduleStatus.error) { return error(); } @@ -37,6 +42,7 @@ extension ScheduleStatusMatch on ScheduleStatus { T Function()? initial, T Function()? loading, T Function()? loaded, + T Function()? selectedDay, T Function()? error}) { final v = this; if (v == ScheduleStatus.initial && initial != null) { @@ -51,6 +57,10 @@ extension ScheduleStatusMatch on ScheduleStatus { return loaded(); } + if (v == ScheduleStatus.selectedDay && selectedDay != null) { + return selectedDay(); + } + if (v == ScheduleStatus.error && error != null) { return error(); } diff --git a/lib/app/pages/schedule/widgets/line_days.dart b/lib/app/pages/schedule/widgets/line_days.dart index 5492206..e535fda 100644 --- a/lib/app/pages/schedule/widgets/line_days.dart +++ b/lib/app/pages/schedule/widgets/line_days.dart @@ -6,6 +6,7 @@ import '../../../core/ui/styles/text_styles.dart'; // ignore: must_be_immutable class LineDays extends StatefulWidget { + final int hoje; final Horario day; final VoidCallback onPressed; Color? color; @@ -15,6 +16,7 @@ class LineDays extends StatefulWidget { required this.day, required this.onPressed, this.color = const Color(0xFF1E1E1E), + required this.hoje, }); @override @@ -60,8 +62,9 @@ class _LineDaysState extends State { ), ), Text( - widget.day.id.toString(), + widget.hoje.toString(), style: TextStyles.instance.texLabelH4.copyWith( + fontSize: context.screenHeight * 0.025, color: ColorsApp.instance.labelblack4, fontWeight: TextStyles.instance.textExtraBold.fontWeight, ), diff --git a/lib/app/pages/schedule/widgets/my_card.dart b/lib/app/pages/schedule/widgets/my_card.dart index 3cadfd0..2a2885d 100644 --- a/lib/app/pages/schedule/widgets/my_card.dart +++ b/lib/app/pages/schedule/widgets/my_card.dart @@ -12,33 +12,50 @@ class MyCard extends StatelessWidget { required this.horarioDetalhado, }); + colorBody() { + if (horarioDetalhado.horario == '18:30 ~ 19:19') { + return ColorsApp.instance.cardwhite; + } else if (horarioDetalhado.horario == '20:20 ~ 21:09') { + return ColorsApp.instance.primary; + } else { + return ColorsApp.instance.cardnoselected; + } + } + + colorLabel() { + if (horarioDetalhado.horario == '18:30 ~ 19:19') { + return ColorsApp.instance.cardnoselected; + } else { + return ColorsApp.instance.cardwhite; + } + } + @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(10), margin: const EdgeInsets.only(bottom: 15, top: 0), decoration: BoxDecoration( - color: ColorsApp.instance.cardnoselected, + color: colorBody(), borderRadius: BorderRadius.circular(5), ), child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - horarioDetalhado.horario, - style: TextStyles.instance.texLabelH4.copyWith( - color: ColorsApp.instance.cardwhite, - fontWeight: TextStyles.instance.textExtraBold.fontWeight), + Container( + padding: EdgeInsets.all(context.screenHeight * 0.005), + decoration: BoxDecoration( + color: ColorsApp.instance.cardred, + borderRadius: BorderRadius.circular(5), + ), + child: Text( + horarioDetalhado.horario, + style: TextStyles.instance.texLabelH4.copyWith( + color: ColorsApp.instance.cardwhite, + fontWeight: TextStyles.instance.textBold.fontWeight, + fontSize: context.screenHeight * 0.017, ), - Text( - horarioDetalhado.sala, - style: TextStyles.instance.texLabelH4.copyWith( - color: ColorsApp.instance.cardwhite, - fontWeight: TextStyles.instance.textExtraBold.fontWeight), - ), - ], + ), ), SizedBox(height: context.screenHeight * 0.015), Row( @@ -48,26 +65,36 @@ class MyCard extends StatelessWidget { child: Text( horarioDetalhado.disciplina, style: TextStyles.instance.texLabelH4.copyWith( - color: ColorsApp.instance.cardwhite, - fontWeight: TextStyles.instance.textSemiBold.fontWeight), + color: colorLabel(), + fontWeight: TextStyles.instance.textSemiBold.fontWeight, + ), ), ), ], ), SizedBox(height: context.screenHeight * 0.015), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Expanded( - child: Text( - horarioDetalhado.professor, - style: TextStyles.instance.texLabelH5.copyWith( - color: ColorsApp.instance.cardwhite, - fontWeight: TextStyles.instance.textSemiBold.fontWeight), - ), - ), - ], + Text( + horarioDetalhado.sala, + style: TextStyles.instance.texLabelH4.copyWith( + color: colorLabel(), + fontWeight: TextStyles.instance.texLabelH4.fontWeight, + ), ), + // SizedBox(height: context.screenHeight * 0.015), + // Row( + // mainAxisAlignment: MainAxisAlignment.start, + // children: [ + // Expanded( + // child: Text( + // 'Nome do Professor', + // style: TextStyles.instance.texLabelH4.copyWith( + // color: colorLabel(), + // fontWeight: TextStyles.instance.texLabelH4.fontWeight, + // ), + // ), + // ), + // ], + // ), ], ), ); diff --git a/lib/app/pages/splash/splash_page.dart b/lib/app/pages/splash/splash_page.dart index f21fa5a..26e7bc3 100644 --- a/lib/app/pages/splash/splash_page.dart +++ b/lib/app/pages/splash/splash_page.dart @@ -16,7 +16,7 @@ class SplashPage extends StatelessWidget { children: [ Image.asset( 'assets/images/logos/logo_ifce.png', - height: context.percentHeight(.25), + height: context.percentWidth(.5), fit: BoxFit.cover, ), SizedBox( @@ -26,7 +26,8 @@ class SplashPage extends StatelessWidget { width: context.percentWidth(.6), height: context.percentHeight(.07), label: 'Acessar', - onPressed: () => Navigator.of(context).popAndPushNamed('/login'), + onPressed: () => + Navigator.of(context).popAndPushNamed('/login'), ), ], ), diff --git a/lib/app/repositories/auth/auth_repository.dart b/lib/app/repositories/auth/auth_repository.dart new file mode 100644 index 0000000..335e9f3 --- /dev/null +++ b/lib/app/repositories/auth/auth_repository.dart @@ -0,0 +1,5 @@ +import 'package:academico_mobile/app/models/auth_model.dart'; + +abstract class AuthRepository { + Future login(String matricula, String password); +} diff --git a/lib/app/repositories/auth/auth_repository_impl.dart b/lib/app/repositories/auth/auth_repository_impl.dart new file mode 100644 index 0000000..9ea0fa1 --- /dev/null +++ b/lib/app/repositories/auth/auth_repository_impl.dart @@ -0,0 +1,40 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:developer'; +import 'package:academico_mobile/app/core/exceptions/repository_exceptions.dart'; +import 'package:academico_mobile/app/core/exceptions/unauthorized_exception.dart'; +import 'package:academico_mobile/app/core/rest_client/custom_dio.dart'; +import 'package:academico_mobile/app/models/auth_model.dart'; +import 'package:dio/dio.dart'; + +import './auth_repository.dart'; + +class AuthRepositoryImpl implements AuthRepository { + final CustomDio dio; + + AuthRepositoryImpl({ + required this.dio, + }); + + @override + Future login(String matricula, String password) async { + try { + final response = await dio.unauth().post( + '/login', + data: {'matricula': matricula, 'password': password}, + ); + final Map jsonData = + response.data as Map; + final String accessToken = jsonData['access_token'] as String; + return accessToken.isNotEmpty + ? AuthModel(accessToken: accessToken) + : throw UnauthorizedException(); + } on DioError catch (e, s) { + if (e.response?.statusCode == 403) { + log('Permissão Negada Repository 1', error: e, stackTrace: s); + throw UnauthorizedException(); + } + log('Erro ao realizar Login Repository', error: e, stackTrace: s); + throw RepositoryExceptions(message: 'Erro ao realizar Login Repository'); + } + } +} diff --git a/lib/app/repositories/daily/daily_repository_impl.dart b/lib/app/repositories/daily/daily_repository_impl.dart index 53892fd..6ee4801 100644 --- a/lib/app/repositories/daily/daily_repository_impl.dart +++ b/lib/app/repositories/daily/daily_repository_impl.dart @@ -18,10 +18,10 @@ class DailyRepositoryImpl implements DailyRepository { @override Future> findDaily() async { try { - final result = await dio.unauth().get('/semestres'); - return (result.data as List) - .map((e) => SemestreModel.fromJson(e as Map)) - .toList(); + final result = await dio.unauth().get('/lista-disciplinas'); + final list = result.data as List; + final semestres = list.map((e) => SemestreModel.fromJson(e)).toList(); + return semestres; } on DioError catch (e, s) { log('Erro ao nuscar semestres', error: e, stackTrace: s); throw RepositoryExceptions(message: 'Erro ao buscar semestres'); diff --git a/lib/app/repositories/schedule/schedule_repository_impl.dart b/lib/app/repositories/schedule/schedule_repository_impl.dart index 191c438..c814178 100644 --- a/lib/app/repositories/schedule/schedule_repository_impl.dart +++ b/lib/app/repositories/schedule/schedule_repository_impl.dart @@ -1,4 +1,5 @@ // ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:convert'; import 'dart:developer'; import 'package:academico_mobile/app/core/exceptions/repository_exceptions.dart'; @@ -19,9 +20,9 @@ class ScheduleRepositoryImpl implements ScheduleRepository { Future> findSchedule() async { try { final result = await dio.unauth().get('/horarios'); - return (result.data as List) - .map((e) => Horario.fromJson(e as Map)) - .toList(); + final data = json.decode(result.data); + final retorno = (data as List).map((e) => Horario.fromJson(e)).toList(); + return retorno; } on DioError catch (e, s) { log('Erro ao buscar horários', error: e, stackTrace: s); throw RepositoryExceptions(message: 'Erro ao buscar horários'); diff --git a/lib/main.dart b/lib/main.dart index 4f019ff..a440e85 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,27 +7,15 @@ Future main() async { runApp(const AcademicoMobile()); } - -//! Código para testes!!!! Apenas para testes!!!! - +//! Teste de código class MyApp extends StatelessWidget { const MyApp({super.key}); - // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( - // This is the theme of your application. - // - // Try running your application with "flutter run". You'll see the - // application has a blue toolbar. Then, without quitting the app, try - // changing the primarySwatch below to Colors.green and then invoke - // "hot reload" (press "r" in the console where you ran "flutter run", - // or simply save your changes to "hot reload" in a Flutter IDE). - // Notice that the counter didn't reset back to zero; the application - // is not restarted. primarySwatch: Colors.blue, ), home: const MyHomePage(title: 'Flutter Demo Home Page'), @@ -38,15 +26,6 @@ class MyApp extends StatelessWidget { class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - final String title; @override @@ -58,47 +37,18 @@ class _MyHomePageState extends State { void _incrementCounter() { setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. _counter++; }); } @override Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. return Scaffold( appBar: AppBar( - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. title: Text(widget.title), ), body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Invoke "debug painting" (press "p" in the console, choose the - // "Toggle Debug Paint" action from the Flutter Inspector in Android - // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) - // to see the wireframe for each widget. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( @@ -115,7 +65,7 @@ class _MyHomePageState extends State { onPressed: _incrementCounter, tooltip: 'Increment', child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. + ), ); } } diff --git a/pubspec.lock b/pubspec.lock index 5117324..fd4ddcd 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,26 +5,26 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "503361166f4a100e0d7eb7fb5a62c6f0322512f2bcb48d6922caf98f24b0ab90" + sha256: "8880b4cfe7b5b17d57c052a5a3a8cc1d4f546261c7cc8fbd717bd53f48db0568" url: "https://pub.dev" source: hosted - version: "56.0.0" + version: "59.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "93fcd81a6716e69864516750590cf1e699420615046bda19100238aa7b429785" + sha256: a89627f49b0e70e068130a36571409726b04dab12da7e5625941d2c8ec278b96 url: "https://pub.dev" source: hosted - version: "5.8.0" + version: "5.11.1" args: dependency: transitive description: name: args - sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440" + sha256: c372bb384f273f0c2a8aaaa226dad84dc27c8519a691b888725dec59518ad53a url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.1" async: dependency: transitive description: @@ -109,10 +109,10 @@ packages: dependency: transitive description: name: built_value - sha256: "31b7c748fd4b9adf8d25d72a4c4a59ef119f12876cf414f94f8af5131d5fa2b0" + sha256: "2f17434bd5d52a26762043d6b43bb53b3acd029b4d9071a329f46d67ef297e6d" url: "https://pub.dev" source: hosted - version: "8.4.4" + version: "8.5.0" characters: dependency: transitive description: @@ -125,10 +125,10 @@ packages: dependency: transitive description: name: checked_yaml - sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311" + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.0.3" clock: dependency: transitive description: @@ -165,10 +165,10 @@ packages: dependency: transitive description: name: crypto - sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" cupertino_icons: dependency: "direct main" description: @@ -181,18 +181,18 @@ packages: dependency: transitive description: name: dart_style - sha256: "6d691edde054969f0e0f26abb1b30834b5138b963793e56f69d3a9a4435e6352" + sha256: f4f1f73ab3fd2afcbcca165ee601fe980d966af6a21b5970c6c9376955c528ad url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.3.1" dio: dependency: "direct main" description: name: dio - sha256: "3e5c4a94d112540d0c9a6b7f3969832e1604eb8cde0f88d0808382f9f632100b" + sha256: "347d56c26d63519552ef9a569f2a593dda99a81fdbdff13c584b7197cfe05059" url: "https://pub.dev" source: hosted - version: "5.0.3" + version: "5.1.2" equatable: dependency: "direct main" description: @@ -209,6 +209,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99 + url: "https://pub.dev" + source: hosted + version: "2.0.2" file: dependency: transitive description: @@ -258,15 +266,20 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: "12006889e2987c549c4c1ec1a5ba4ec4b24d34d2469ee5f9476c926dcecff266" + sha256: f991fdb1533c3caeee0cdc14b04f50f0c3916f0dbcbc05237ccbe4e3c6b93f3f url: "https://pub.dev" source: hosted - version: "2.0.4" + version: "2.0.5" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" frontend_server_client: dependency: transitive description: @@ -327,10 +340,10 @@ packages: dependency: transitive description: name: json_annotation - sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317 + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 url: "https://pub.dev" source: hosted - version: "4.8.0" + version: "4.8.1" lints: dependency: transitive description: @@ -443,6 +456,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1" + url: "https://pub.dev" + source: hosted + version: "2.1.10" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" + url: "https://pub.dev" + source: hosted + version: "2.0.6" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: d3f80b32e83ec208ac95253e0cd4d298e104fbc63cb29c5c69edaed43b0c69d6 + url: "https://pub.dev" + source: hosted + version: "2.1.6" petitparser: dependency: transitive description: @@ -451,6 +488,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.1.0" + platform: + dependency: transitive + description: + name: platform + sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + url: "https://pub.dev" + source: hosted + version: "3.1.0" plugin_platform_interface: dependency: transitive description: @@ -467,6 +512,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" + process: + dependency: transitive + description: + name: process + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" + source: hosted + version: "4.2.4" provider: dependency: "direct main" description: @@ -479,34 +532,90 @@ packages: dependency: transitive description: name: pub_semver - sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" pubspec_parse: dependency: transitive description: name: pubspec_parse - sha256: ec85d7d55339d85f44ec2b682a82fea340071e8978257e5a43e69f79e98ef50c + sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.3" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "16d3fb6b3692ad244a695c0183fca18cf81fd4b821664394a781de42386bf022" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "6478c6bbbecfe9aced34c483171e90d7c078f5883558b30ec3163cf18402c749" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: e014107bb79d6d3297196f4f2d0db54b5d1f85b8ea8ff63b8e8b391a02700feb + url: "https://pub.dev" + source: hosted + version: "2.2.2" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "9d387433ca65717bbf1be88f4d5bb18f10508917a8fa2fb02e0fd0d7479a9afa" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: fb5cf25c0235df2d0640ac1b1174f6466bd311f621574997ac59018a6664548d + url: "https://pub.dev" + source: hosted + version: "2.2.0" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: "74083203a8eae241e0de4a0d597dbedab3b8fef5563f33cf3c12d7e93c655ca5" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "5e588e2efef56916a3b229c3bfe81e6a525665a454519ca51dbcc4236a274173" + url: "https://pub.dev" + source: hosted + version: "2.2.0" shelf: dependency: transitive description: name: shelf - sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8 + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" sky_engine: dependency: transitive description: flutter @@ -516,10 +625,10 @@ packages: dependency: transitive description: name: source_gen - sha256: c2bea18c95cfa0276a366270afaa2850b09b4a76db95d546f3d003dcc7011298 + sha256: b20e191de6964e98032573cecb1d2b169d96ba63fdb586d24dcd1003ba7e94f6 url: "https://pub.dev" source: hosted - version: "1.2.7" + version: "1.3.0" source_span: dependency: transitive description: @@ -588,10 +697,10 @@ packages: dependency: "direct main" description: name: toggle_switch - sha256: "82c778c4bfe93af154a41e346ccd1d5b0cb6a50f8f187941412edd0a9bbf51ee" + sha256: "9e6af1f0c5a97d9de41109dc7b9e1b3bbe73417f89b10e0e44dc834fb493d4cb" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" top_snackbar_flutter: dependency: "direct main" description: @@ -608,30 +717,38 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + validatorless: + dependency: "direct main" + description: + name: validatorless + sha256: ddb46df636114b3322d289489164cac309767b157191ba43c7ad49b28c2b57c7 + url: "https://pub.dev" + source: hosted + version: "1.2.3" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: "4cf8e60dbe4d3a693d37dff11255a172594c0793da542183cbfe7fe978ae4aaa" + sha256: ea8d3fc7b2e0f35de38a7465063ecfcf03d8217f7962aa2a6717132cb5d43a79 url: "https://pub.dev" source: hosted - version: "1.1.4" + version: "1.1.5" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: "278ad5f816f58b1967396d1f78ced470e3e58c9fe4b27010102c0a595c764468" + sha256: a5eaa5d19e123ad4f61c3718ca1ed921c4e6254238d9145f82aa214955d9aced url: "https://pub.dev" source: hosted - version: "1.1.4" + version: "1.1.5" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: "0bf61ad56e6fd6688a2865d3ceaea396bc6a0a90ea0d7ad5049b1b76c09d6163" + sha256: "15edc42f7eaa478ce854eaf1fbb9062a899c0e4e56e775dd73b7f4709c97c4ca" url: "https://pub.dev" source: hosted - version: "1.1.4" + version: "1.1.5" vector_math: dependency: transitive description: @@ -652,42 +769,58 @@ packages: dependency: transitive description: name: web_socket_channel - sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.4.0" webview_flutter: dependency: "direct main" description: name: webview_flutter - sha256: "47663d51a9061451aa3880a214ee9a65dcbb933b77bc44388e194279ab3ccaf6" + sha256: "1a37bdbaaf5fbe09ad8579ab09ecfd473284ce482f900b5aea27cf834386a567" url: "https://pub.dev" source: hosted - version: "4.0.7" + version: "4.2.0" webview_flutter_android: dependency: transitive description: name: webview_flutter_android - sha256: "34f83c2f0f64c75ad75c77a2ccfc8d2e531afbe8ad41af1fd787d6d33336aa90" + sha256: "1acea8def62592123e2fbbca164ed8681a98a890bdcbb88f916d5b4a22687759" url: "https://pub.dev" source: hosted - version: "3.4.3" + version: "3.7.0" webview_flutter_platform_interface: dependency: transitive description: name: webview_flutter_platform_interface - sha256: "1939c39e2150fb4d30fd3cc59a891a49fed9935db53007df633ed83581b6117b" + sha256: "78715dc442b7849dbde74e92bb67de1cecf5addf95531c5fb474e72f5fe9a507" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.3.0" webview_flutter_wkwebview: dependency: transitive description: name: webview_flutter_wkwebview - sha256: ab12479f7a0cf112b9420c36aaf206a1ca47cd60cd42de74a4be2e97a697587b + sha256: "61f33512810bf1ee9ac89761a4b02663ff64e8227b7dc80654642acd660fd49d" url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.4.2" + win32: + dependency: transitive + description: + name: win32 + sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c" + url: "https://pub.dev" + source: hosted + version: "4.1.4" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1 + url: "https://pub.dev" + source: hosted + version: "1.0.0" xml: dependency: transitive description: @@ -700,10 +833,10 @@ packages: dependency: transitive description: name: yaml - sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.2" sdks: dart: ">=2.19.0 <4.0.0" flutter: ">=3.7.0-0" diff --git a/pubspec.yaml b/pubspec.yaml index 7b9ad98..f800b7f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,6 +22,8 @@ dependencies: equatable: ^2.0.5 match: ^0.4.1 toggle_switch: ^2.0.1 + validatorless: ^1.2.3 + shared_preferences: ^2.1.0 dev_dependencies: flutter_test: