This commit is contained in:
J. A. Messias 2024-12-09 16:24:27 -03:00
parent cb352b42ff
commit 13f07a237c
19 changed files with 785 additions and 0 deletions

View File

@ -0,0 +1,3 @@
export 'data_sources/index.dart';
export 'repositories/index.dart';
export 'models/index.dart';

View File

@ -0,0 +1,3 @@
export 'entities/index.dart';
export 'respositories/index.dart';
export 'usecases/index.dart';

View File

@ -0,0 +1,3 @@
export 'data/index.dart';
export 'domain/index.dart';
export 'presentation/index.dart';

View File

@ -0,0 +1,117 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hub/backend/api_requests/api_manager.dart';
import 'package:hub/flutter_flow/nav/nav.dart';
import 'package:hub/shared/utils/dialog_util.dart';
import 'package:hub/shared/utils/log_util.dart';
class HistoryBloc extends Bloc<HistoryEvent, HistoryState> {
final ScrollController scrollController = ScrollController();
final Future<ApiCallResponse> Function(int pageSize, int pageNumber, Map<String, String> opt) fetchHistoryService;
final bool Function(dynamic item, Map<String, String> opt)? filterLogic;
HistoryBloc(Map<String, String> opt, this.fetchHistoryService, {this.filterLogic}) : super(HistoryState(opt: opt)) {
scrollController.addListener(_onScroll);
on<UpdateHistoryEvent>(_onUpdateHistory);
on<FetchHistoryEvent>(_onFetchHistory);
add(FetchHistoryEvent());
}
void _onScroll() {
if (scrollController.position.atEdge && scrollController.position.pixels != 0) {
add(FetchHistoryEvent());
}
}
Future<void> _onUpdateHistory(UpdateHistoryEvent event, Emitter<HistoryState> emit) async {
emit(state.copyWith(opt: event.newOpt, historyWrap: [], pageNumber: 1));
add(FetchHistoryEvent());
}
Future<void> _onFetchHistory(FetchHistoryEvent event, Emitter<HistoryState> emit) async {
if (state.loading) return;
emit(state.copyWith(loading: true));
try {
var response = await fetchHistoryService(state.pageSize, state.pageNumber, state.opt);
final List<dynamic> history = response.jsonBody['acessos'] ?? [];
List<dynamic> filteredHistory = history.where((item) {
if (filterLogic != null) {
return filterLogic!(item, state.opt);
} else {
return true; // Default to no filtering if no logic is provided
}
}).toList();
if (filteredHistory.isNotEmpty) {
emit(state.copyWith(
historyWrap: [...state.historyWrap, ...filteredHistory],
hasData: true,
loading: false,
pageNumber: state.pageNumber + 1,
));
} else {
emit(state.copyWith(hasData: false, loading: false));
}
} catch (e, s) {
await DialogUtil.errorDefault(navigatorKey.currentContext!);
LogUtil.requestAPIFailed('processRequest', "", "Fetch History", e, s);
emit(state.copyWith(hasData: false, loading: false, error: e.toString()));
}
}
@override
Future<void> close() {
scrollController.dispose();
return super.close();
}
}
class HistoryState {
final Map<String, String> opt;
final List<dynamic> historyWrap;
final bool hasData;
final bool loading;
final int pageNumber;
final int pageSize;
final String? error;
HistoryState({
required this.opt,
this.historyWrap = const [],
this.hasData = false,
this.loading = false,
this.pageNumber = 1,
this.pageSize = 10,
this.error,
});
HistoryState copyWith({
Map<String, String>? opt,
List<dynamic>? historyWrap,
bool? hasData,
bool? loading,
int? pageNumber,
int? pageSize,
String? error,
}) {
return HistoryState(
opt: opt ?? this.opt,
historyWrap: historyWrap ?? this.historyWrap,
hasData: hasData ?? this.hasData,
loading: loading ?? this.loading,
pageNumber: pageNumber ?? this.pageNumber,
pageSize: pageSize ?? this.pageSize,
error: error ?? this.error,
);
}
}
abstract class HistoryEvent {}
class UpdateHistoryEvent extends HistoryEvent {
final Map<String, String> newOpt;
UpdateHistoryEvent(this.newOpt);
}
class FetchHistoryEvent extends HistoryEvent {}

View File

@ -0,0 +1,2 @@
export 'history_bloc_template.dart';
export 'provisional_history_bloc.dart';

View File

@ -0,0 +1,3 @@
export 'blocs/index.dart';
export 'pages/index.dart';
export 'widgets/index.dart';

View File

@ -0,0 +1,106 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hub/backend/api_requests/api_calls.dart';
import 'package:hub/components/molecular_components/message_opt_modal/opt_modal_widget.dart';
import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart';
import 'package:hub/features/history/index.dart';
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
import 'package:hub/flutter_flow/internationalization.dart';
import 'package:hub/shared/helpers/storage/base_storage.dart';
import 'package:hub/shared/helpers/storage/storage_helper.dart';
class AcessCubit extends Cubit<String?> {
AcessCubit() : super('Provisional');
Future<void> fetchCliUUID() async {
final cliUUID = await StorageHelper().get(KeychainStorageKey.clientUUID.value);
emit(cliUUID);
}
Future<ApiCallResponse> fetchHistoryService(int pageSize, int pageNumber, Map<String, String> opt) async {
return await PhpGroup.getAccessCall.call(
pageSize: pageSize.toString(),
pageNumber: pageNumber.toString(),
pesTipo: opt['personType'] != 'E' && opt['personType'] != 'O' ? 'T' : opt['personType'],
);
}
bool filterLogic(dynamic item, Map<String, String> opt) {
final personTypeMatches = opt['personType'] == '.*' || item["PES_TIPO"].toString() == opt['personType'];
return personTypeMatches;
}
}
class AccessHistoryPage extends StatelessWidget {
final Map<String, String> opt = {
'personType': '.*',
'accessType': '.*',
'search': '.*',
};
final String appBarTitle = 'Histórico Provisório';
final String noHistoryMessage = 'Nenhum histórico encontrado!';
final String errorMessage = 'Falha ao realizar operação!';
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => AcessCubit()..fetchCliUUID(),
child: HistoryPageTemplate(
opt: opt,
fetchHistoryService: (pageSize, pageNumber, opt) => context.read<AcessCubit>().fetchHistoryService(pageSize, pageNumber, opt),
cardBuilder: cardBuilder,
appBarTitle: appBarTitle,
noHistoryMessage: noHistoryMessage,
errorMessage: errorMessage,
isLeading: true,
isFilter: true,
filterLogic: (item, opt) => context.read<AcessCubit>().filterLogic(item, opt),
),
);
}
Widget cardBuilder(BuildContext context, dynamic historyItem) {
final cliUUID = context.watch<AcessCubit>().state;
final String imagePath = 'https://freaccess.com.br/freaccess/getImage.php?cliID=${cliUUID}&atividade=getFoto&Documento=${historyItem['PES_ID'] ?? ''}&tipo=${historyItem['PES_TIPO'] ?? ''}';
final Map<String, String> labelsHashMap = {
FFLocalizations.of(context).getVariableText(ptText: 'Nome:', enText: 'Name:'): historyItem['PES_NOME'] ?? '',
FFLocalizations.of(context).getVariableText(ptText: 'Acesso:', enText: 'Access:'): historyItem['ACE_DATAHORA'] ?? '',
FFLocalizations.of(context).getVariableText(ptText: 'Setor', enText: 'Sector'): historyItem['SET_DESCRICAO'] ?? '',
};
final statusHashMap = [
_getPersonTypeStatus(context, historyItem['PES_TIPO']),
_getAccessTypeStatus(context, historyItem['ACE_TIPO']),
];
return CardItemTemplateComponentWidget(
imagePath: imagePath,
labelsHashMap: labelsHashMap,
statusHashMap: statusHashMap,
onTapCardItemAction: () async {},
);
}
Map<String, Color> _getPersonTypeStatus(BuildContext context, String? personType) {
switch (personType) {
case 'O':
return {FFLocalizations.of(context).getVariableText(ptText: 'Morador', enText: 'Resident'): FlutterFlowTheme.of(context).alternate2};
case 'E':
return {FFLocalizations.of(context).getVariableText(ptText: 'Visitante', enText: 'Visitor'): FlutterFlowTheme.of(context).alternate2};
default:
return {FFLocalizations.of(context).getVariableText(ptText: 'Desconhecido', enText: 'Unknown'): FlutterFlowTheme.of(context).alternate2};
}
}
Map<String, Color> _getAccessTypeStatus(BuildContext context, String? accessType) {
switch (accessType) {
case '0':
return {FFLocalizations.of(context).getVariableText(ptText: 'Entrada', enText: 'Entrance'): FlutterFlowTheme.of(context).success};
case '1':
return {FFLocalizations.of(context).getVariableText(ptText: 'Saída', enText: 'Exit'): FlutterFlowTheme.of(context).error};
default:
return {FFLocalizations.of(context).getVariableText(ptText: 'Desconhecido', enText: 'Unknown'): FlutterFlowTheme.of(context).warning};
}
}
}

View File

@ -0,0 +1,211 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:hub/features/history/index.dart';
import 'package:hub/flutter_flow/internationalization.dart';
import 'package:hub/flutter_flow/nav/nav.dart';
import 'package:hub/backend/api_requests/api_manager.dart';
import 'package:hub/features/history/presentation/widgets/filter_modal.dart';
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
import 'package:hub/shared/utils/dialog_util.dart';
import 'package:hub/shared/utils/log_util.dart';
import 'package:hub/flutter_flow/flutter_flow_icon_button.dart';
class HistoryPageTemplate extends StatelessWidget {
final Map<String, String> opt;
final Future<ApiCallResponse> Function(int pageSize, int pageNumber, Map<String, String> opt) fetchHistoryService;
final Widget Function(BuildContext context, dynamic historyItem) cardBuilder;
final String appBarTitle;
final String noHistoryMessage;
final String errorMessage;
final bool isLeading;
final bool isFilter;
final bool Function(dynamic item, Map<String, String> opt)? filterLogic;
const HistoryPageTemplate({
Key? key,
required this.opt,
required this.fetchHistoryService,
required this.cardBuilder,
this.appBarTitle = 'History',
this.noHistoryMessage = 'No history found!',
this.errorMessage = 'Failed to perform operation!',
this.isLeading = true,
this.isFilter = false,
this.filterLogic,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => HistoryBloc(opt, fetchHistoryService, filterLogic: filterLogic),
child: HistoryPageView(
cardBuilder: cardBuilder,
appBarTitle: appBarTitle,
noHistoryMessage: noHistoryMessage,
errorMessage: errorMessage,
isLeading: isLeading,
isFilter: isFilter,
),
);
}
}
class HistoryPageView extends StatelessWidget {
final Widget Function(BuildContext context, dynamic historyItem) cardBuilder;
final String appBarTitle;
final String noHistoryMessage;
final String errorMessage;
final bool isLeading;
final bool isFilter;
const HistoryPageView({
Key? key,
required this.cardBuilder,
required this.appBarTitle,
required this.noHistoryMessage,
required this.errorMessage,
this.isLeading = false,
this.isFilter = false,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = FlutterFlowTheme.of(context);
return Scaffold(
key: GlobalKey<ScaffoldState>(),
backgroundColor: theme.primaryBackground,
appBar: _buildAppBar(context, theme),
body: _buildBody(context),
);
}
PreferredSizeWidget _buildAppBar(BuildContext context, FlutterFlowTheme theme) {
return AppBar(
backgroundColor: theme.primaryBackground,
automaticallyImplyLeading: false,
leading: isLeading ? _buildBackButton(context, theme) : null,
title: _buildTitle(context, theme),
centerTitle: true,
elevation: 0.0,
actions: [
if (isFilter) _buildFilterButton(context),
],
);
}
Widget _buildBackButton(BuildContext context, FlutterFlowTheme theme) {
return FlutterFlowIconButton(
borderColor: Colors.transparent,
borderRadius: 30.0,
borderWidth: 1.0,
buttonSize: 60.0,
icon: Icon(
Icons.keyboard_arrow_left,
color: theme.primaryText,
size: 30.0,
),
onPressed: () => Navigator.of(context).pop(),
);
}
Widget _buildTitle(BuildContext context, FlutterFlowTheme theme) {
return Text(
appBarTitle,
style: theme.headlineMedium.override(
fontFamily: theme.headlineMediumFamily,
color: theme.primaryText,
fontSize: 16.0,
letterSpacing: 0.0,
useGoogleFonts: GoogleFonts.asMap().containsKey(theme.headlineMediumFamily),
),
);
}
Widget _buildFilterButton(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 10, 0),
child: IconButton(
icon: const Icon(Icons.filter_list),
onPressed: () async {
final Map<String, String>? selectedFilter = await showModalBottomSheet<Map<String, String>>(
isScrollControlled: true,
backgroundColor: Colors.transparent,
context: context,
builder: (BuildContext bottomSheetContext) {
return GestureDetector(
onTap: () => Navigator.of(bottomSheetContext).pop(),
child: Container(
color: Colors.transparent,
child: GestureDetector(
onTap: () {},
child: FilterModalWidget(
defaultSelections: {
'personType': context.read<HistoryBloc>().state.opt['personType'] == '.*' ? ['E', 'O'] : [context.read<HistoryBloc>().state.opt['personType']],
'search': '.*',
},
filterOptions: {
'personType': [
{'title': FFLocalizations.of(context).getVariableText(ptText: 'Visitante', enText: 'Visitor'), 'value': 'E',},
{'title': FFLocalizations.of(context).getVariableText(ptText: 'Morador', enText: 'Resident'), 'value': 'O',},
],
},
filterTitles: {
'personType': 'Person Type',
},
),
),
),
);
},
);
if (selectedFilter != null) {
context.read<HistoryBloc>().add(UpdateHistoryEvent(selectedFilter));
}
},
),
),
],
);
}
Widget _buildBody(BuildContext context) {
return BlocBuilder<HistoryBloc, HistoryState>(
builder: (context, state) {
if (state.loading && state.historyWrap.isEmpty) {
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
FlutterFlowTheme.of(context).primary,
),
),
);
} else if (state.error != null) {
return Center(
child: Text(errorMessage),
);
} else if (state.historyWrap.isEmpty) {
return Center(
child: Text(noHistoryMessage),
);
} else {
return ListView.builder(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
controller: context.read<HistoryBloc>().scrollController,
itemCount: state.historyWrap.length,
itemBuilder: (context, index) {
final historyItem = state.historyWrap[index];
return cardBuilder(context, historyItem);
},
);
}
},
);
}
}

View File

@ -0,0 +1,2 @@
export 'history_page_template.dart';
export 'provisional_history_page.dart';

View File

@ -0,0 +1,105 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hub/backend/api_requests/api_calls.dart';
import 'package:hub/components/molecular_components/message_opt_modal/opt_modal_widget.dart';
import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart';
import 'package:hub/features/history/index.dart';
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
import 'package:hub/flutter_flow/internationalization.dart';
import 'package:hub/shared/helpers/storage/base_storage.dart';
import 'package:hub/shared/helpers/storage/storage_helper.dart';
class ProvisionalCubit extends Cubit<String?> {
ProvisionalCubit() : super('Provisional');
Future<void> fetchCliUUID() async {
final cliUUID = await StorageHelper().get(KeychainStorageKey.clientUUID.value);
emit(cliUUID);
}
Future<ApiCallResponse> fetchHistoryService(int pageSize, int pageNumber, Map<String, String> status) async {
return await PhpGroup.getProvSchedules(pageNumber.toString(), '');
}
bool filterLogic(dynamic item, Map<String, String> opt) {
final personTypeMatches = opt['personType'] == '.*' || item["PES_TIPO"].toString() == opt['personType'];
return personTypeMatches;
}
}
class ProvisionalHistoryPage extends StatelessWidget {
final Map<String, String> opt = {
'personType': '.*',
'accessType': '.*',
'search': '.*',
};
final String appBarTitle = 'Histórico Provisório';
final String noHistoryMessage = 'Nenhum histórico encontrado!';
final String errorMessage = 'Falha ao realizar operação!';
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => ProvisionalCubit()..fetchCliUUID(),
child: HistoryPageTemplate(
opt: opt,
fetchHistoryService: (pageSize, pageNumber, opt) => context.read<ProvisionalCubit>().fetchHistoryService(pageSize, pageNumber, opt),
cardBuilder: cardBuilder,
appBarTitle: appBarTitle,
noHistoryMessage: noHistoryMessage,
errorMessage: errorMessage,
isLeading: true,
isFilter: false,
filterLogic: (item, opt) => context.read<ProvisionalCubit>().filterLogic(item, opt),
),
);
}
Widget cardBuilder(BuildContext context, dynamic historyItem) {
log('historyItem: $historyItem');
final cliUUID = context.watch<ProvisionalCubit>().state;
final String imagePath = 'https://freaccess.com.br/freaccess/getImage.php?cliID=${cliUUID}&atividade=getFoto&Documento=${historyItem['PES_ID'] ?? ''}&tipo=${historyItem['PES_TIPO'] ?? ''}';
final Map<String, String> labelsHashMap = {
FFLocalizations.of(context).getVariableText(ptText: 'Nome:', enText: 'Name:'): historyItem['PES_NOME'] ?? '',
FFLocalizations.of(context).getVariableText(ptText: 'Acesso:', enText: 'Access:'): historyItem['ACE_DATAHORA'] ?? '',
FFLocalizations.of(context).getVariableText(ptText: 'Setor', enText: 'Sector'): historyItem['SET_DESCRICAO'] ?? '',
};
final statusHashMap = [
_getPersonTypeStatus(context, historyItem['PES_TIPO']),
_getAccessTypeStatus(context, historyItem['ACE_TIPO']),
];
return CardItemTemplateComponentWidget(
imagePath: imagePath,
labelsHashMap: labelsHashMap,
statusHashMap: statusHashMap,
onTapCardItemAction: () async {},
);
}
Map<String, Color> _getPersonTypeStatus(BuildContext context, String? personType) {
switch (personType) {
case 'O':
return {FFLocalizations.of(context).getVariableText(ptText: 'Morador', enText: 'Resident'): FlutterFlowTheme.of(context).alternate2};
case 'E':
return {FFLocalizations.of(context).getVariableText(ptText: 'Visitante', enText: 'Visitor'): FlutterFlowTheme.of(context).alternate2};
default:
return {FFLocalizations.of(context).getVariableText(ptText: 'Desconhecido', enText: 'Unknown'): FlutterFlowTheme.of(context).alternate2};
}
}
Map<String, Color> _getAccessTypeStatus(BuildContext context, String? accessType) {
switch (accessType) {
case '0':
return {FFLocalizations.of(context).getVariableText(ptText: 'Entrada', enText: 'Entrance'): FlutterFlowTheme.of(context).success};
case '1':
return {FFLocalizations.of(context).getVariableText(ptText: 'Saída', enText: 'Exit'): FlutterFlowTheme.of(context).error};
default:
return {FFLocalizations.of(context).getVariableText(ptText: 'Desconhecido', enText: 'Unknown'): FlutterFlowTheme.of(context).warning};
}
}
}

View File

@ -0,0 +1,229 @@
import 'package:flutter/material.dart';
import '/flutter_flow/flutter_flow_util.dart';
import '/flutter_flow/form_field_controller.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
import 'package:hub/flutter_flow/nav/nav.dart';
import 'package:hub/shared/utils/limited_text_size.dart';
class FilterModal extends FlutterFlowModel<FilterModalWidget> {
FocusNode? textFieldFocusNode;
TextEditingController? textController;
String? Function(BuildContext, String?)? textControllerValidator;
bool? checkboxValue1;
bool? checkboxValue2;
FormFieldController<List<String>>? checkboxGroupValueController;
List<String>? get checkboxGroupValues => checkboxGroupValueController?.value;
set checkboxGroupValues(List<String>? v) => checkboxGroupValueController?.value = v;
@override
void initState(BuildContext context) {}
@override
void dispose() {
textFieldFocusNode?.dispose();
textController?.dispose();
}
}
class FilterModalWidget extends StatefulWidget {
final Map<String, dynamic> defaultSelections;
final Map<String, List<Map<String, String>>> filterOptions;
final Map<String, String> filterTitles;
const FilterModalWidget({
super.key,
required this.defaultSelections,
required this.filterOptions,
required this.filterTitles,
});
@override
_FilterModalWidgetState createState() => _FilterModalWidgetState();
}
class _FilterModalWidgetState extends State<FilterModalWidget> {
late FilterModal _model;
late Map<String, dynamic> selected;
@override
void setState(VoidCallback callback) {
super.setState(callback);
_model.onUpdate();
}
@override
void initState() {
super.initState();
_model = createModel(context, () => FilterModal());
_model.textController ??= TextEditingController();
_model.textFieldFocusNode ??= FocusNode();
selected = widget.defaultSelections;
}
void _applyFilter() {
Map<String, String> filterResult = {
'search': _model.textController?.text == '' ? '.*' : _model.textController!.text.toLowerCase(),
};
widget.filterOptions.forEach((key, options) {
filterResult[key] = selected[key]!.isEmpty || selected[key]!.length > 1
? '.*'
: selected[key]!.first;
});
context.pop(filterResult);
}
Widget _buildCheckboxListTile(String key, List<Map<String, String>> options, double fontsize) {
double limitedInputFontSize = LimitedFontSizeUtil.getInputFontSize(context);
return Column(
children: [
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsetsDirectional.fromSTEB(0.0, 3.0, 0.0, 0.0),
child: Text(
widget.filterTitles[key]!,
textAlign: TextAlign.left,
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily,
fontSize: limitedInputFontSize,
letterSpacing: 0.0,
useGoogleFonts: GoogleFonts.asMap().containsKey(FlutterFlowTheme.of(context).bodyMediumFamily),
color: FlutterFlowTheme.of(context).primaryText,
),
),
),
],
),
ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: options.length,
itemBuilder: (context, index) {
final option = options[index];
return CheckboxListTile(
title: Text(
option['title']!,
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily,
letterSpacing: 0.0,
fontSize: limitedInputFontSize,
useGoogleFonts: GoogleFonts.asMap().containsKey(FlutterFlowTheme.of(context).bodyMediumFamily),
color: FlutterFlowTheme.of(context).primaryText,
),
),
dense: true,
value: selected[key]!.contains(option['value']),
onChanged: (bool? value) {
setState(() {
if (value == true) {
if (!selected[key]!.contains(option['value'])) {
selected[key]!.add(option['value']);
}
} else {
selected[key]!.remove(option['value']);
}
});
},
activeColor: FlutterFlowTheme.of(context).primary,
checkColor: FlutterFlowTheme.of(context).info,
checkboxShape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100),
),
enableFeedback: true,
side: BorderSide(
width: 5,
color: FlutterFlowTheme.of(context).secondaryText,
),
controlAffinity: ListTileControlAffinity.leading,
);
},
),
],
);
}
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
return Center(
child: Container(
width: screenWidth - (screenWidth * 0.35),
height: 250,
decoration: BoxDecoration(
color: FlutterFlowTheme.of(context).primaryBackground,
borderRadius: BorderRadius.circular(24.0),
),
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Column(
children: [
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsetsDirectional.fromSTEB(10.0, 10.0, 0.0, 10.0),
child: Text(
FFLocalizations.of(context).getText('yfj9pd6k'),
style: FlutterFlowTheme.of(context).headlineMedium.override(
fontFamily: FlutterFlowTheme.of(context).headlineMediumFamily,
color: FlutterFlowTheme.of(context).primaryText,
fontSize: LimitedFontSizeUtil.getHeaderFontSize(context),
letterSpacing: 0.0,
fontWeight: FontWeight.bold,
useGoogleFonts:
GoogleFonts.asMap().containsKey(FlutterFlowTheme.of(context).headlineMediumFamily),
),
),
),
],
),
Expanded(
child: SingleChildScrollView(
child: Container(
padding: const EdgeInsets.all(10),
child: Column(
mainAxisSize: MainAxisSize.min,
children: widget.filterOptions.keys.map((key) {
return _buildCheckboxListTile(key, widget.filterOptions[key]!, 14);
}).toList(),
),
),
),
),
ElevatedButton(
onPressed: _applyFilter,
style: ElevatedButton.styleFrom(
foregroundColor: FlutterFlowTheme.of(context).info,
backgroundColor: FlutterFlowTheme.of(context).primary,
),
child: Text(FFLocalizations.of(context).getText('88kshkph'),
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily,
color: FlutterFlowTheme.of(context).info,
fontSize: LimitedFontSizeUtil.getInputFontSize(context),
letterSpacing: 0.0,
fontWeight: FontWeight.bold,
useGoogleFonts:
GoogleFonts.asMap().containsKey(FlutterFlowTheme.of(context).bodyMediumFamily),
)),
),
],
),
),
),
);
}
}

View File

@ -0,0 +1 @@
export 'filter_modal.dart';