Skip to content

Commit fe74969

Browse files
committed
add: dummy data repo for form submission
1 parent fe6a521 commit fe74969

File tree

9 files changed

+212
-82
lines changed

9 files changed

+212
-82
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import 'package:dynamicform/business_logic/post_form_cubit/post_form_state.dart';
2+
import 'package:dynamicform/data_repository/form_repository.dart';
3+
import 'package:flutter/foundation.dart';
4+
import 'package:flutter_bloc/flutter_bloc.dart';
5+
6+
class PostFormCubit extends Cubit<PostFormState> {
7+
final FormRepository _formRepository;
8+
9+
PostFormCubit(this._formRepository) : super(PostFormInitialState());
10+
11+
/// Stores the unique identifier for the form in the database.
12+
/// This ID is both embedded in the form JSON and used as the filename in the database.
13+
/// Set when fetching form templates and accessing individual forms.
14+
String? formIdInDB;
15+
16+
Future<void> postForm({required Map<String, dynamic> formData}) async {
17+
emit(PostingFormState());
18+
debugPrint("FORMTOSUBMIT: $formIdInDB");
19+
final response = await _formRepository.postForm(
20+
formIdInDB ?? "",
21+
formData,
22+
);
23+
response.fold((success) {
24+
emit(PostFormSuccessState(success));
25+
}, (failure) {
26+
emit(PostFormFailureState(failure));
27+
});
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import 'package:dynamicform/core/model/response_models/post_form_success_model.dart';
2+
import 'package:dynamicform/core/network/failure/failure_model.dart';
3+
4+
abstract class PostFormState {}
5+
6+
class PostFormInitialState extends PostFormState {}
7+
8+
class PostingFormState extends PostFormState {}
9+
10+
class PostFormSuccessState extends PostFormState {
11+
final PostFormSuccessModel postFormSuccessModel;
12+
13+
PostFormSuccessState(this.postFormSuccessModel);
14+
}
15+
16+
class PostFormFailureState extends PostFormState {
17+
final Failure failure;
18+
19+
PostFormFailureState(this.failure);
20+
}

lib/core/constants/text_constants.dart

+1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ class TextConstants {
66
static const String nA = "N/A";
77
static const String formTemplates = "Form Templates";
88
static const String noFormsAvailable = "No forms available";
9+
static const String formSubmittedSuccessfully = "Form submitted successfully";
910
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import 'package:dynamicform/core/constants/text_constants.dart';
2+
import 'package:flutter/foundation.dart';
3+
4+
class PostFormSuccessModel {
5+
String? message;
6+
String? submissionId;
7+
DateTime? submittedDate;
8+
9+
PostFormSuccessModel({this.message, this.submissionId, this.submittedDate});
10+
11+
static const String kMessage = "message";
12+
static const String kSubmissionId = "submissionId";
13+
static const String kSubmittedDate = "submittedDate";
14+
15+
PostFormSuccessModel.fromJson(Map<String, dynamic> json) {
16+
message = json[kMessage] ?? TextConstants.formSubmittedSuccessfully;
17+
submissionId = json[kSubmissionId];
18+
try {
19+
submittedDate = DateTime.parse(json[kSubmittedDate]);
20+
} catch (e) {
21+
debugPrint("Error parsing submittedDate: $e");
22+
submittedDate = null;
23+
}
24+
}
25+
}
+3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import 'package:dartz/dartz.dart';
22
import 'package:dynamicform/core/model/form_model.dart';
33
import 'package:dynamicform/core/model/response_models/forms_response_model.dart';
4+
import 'package:dynamicform/core/model/response_models/post_form_success_model.dart';
45
import 'package:dynamicform/core/network/failure/failure_model.dart';
56

67
abstract class FormRepository {
78
Future<Either<FormsResponseModel, Failure>> getForms();
89
Future<Either<DynamicFormModel, Failure>> getForm(String formId);
10+
Future<Either<PostFormSuccessModel, Failure>> postForm(
11+
String formId, Map<String, dynamic> formData);
912
}

lib/data_repository/form_repository_implementation.dart

+13
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'package:dartz/dartz.dart';
22
import 'package:dynamicform/core/constants/network_constants.dart';
33
import 'package:dynamicform/core/model/form_model.dart';
44
import 'package:dynamicform/core/model/response_models/forms_response_model.dart';
5+
import 'package:dynamicform/core/model/response_models/post_form_success_model.dart';
56
import 'package:dynamicform/core/network/client/base_client.dart';
67
import 'package:dynamicform/core/network/failure/failure_model.dart';
78
import 'package:dynamicform/core/network/functions/get_parsed_data.dart';
@@ -29,4 +30,16 @@ class FormRepositoryImplementation extends FormRepository {
2930
path: "${NetworkConstants.kForms}/$formId");
3031
return getParsedData(response, DynamicFormModel.fromJson);
3132
}
33+
34+
@override
35+
Future<Either<PostFormSuccessModel, Failure>> postForm(
36+
String formId, Map<String, dynamic> formData) async {
37+
final response = await _client.postRequest(
38+
data: formData,
39+
requiresAuthorization: false,
40+
showDialog: true,
41+
baseUrl: NetworkConstants.baseUrl,
42+
path: "${NetworkConstants.kForms}/$formId/submit");
43+
return getParsedData(response, PostFormSuccessModel.fromJson);
44+
}
3245
}

lib/dynamic_form_app.dart

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:dynamicform/business_logic/get_form_cubit/get_form_cubit.dart';
22
import 'package:dynamicform/business_logic/get_forms_cubit/get_forms_cubit.dart';
3+
import 'package:dynamicform/business_logic/post_form_cubit/post_form_cubit.dart';
34
import 'package:dynamicform/core/routes/route_generator.dart';
45
import 'package:dynamicform/core/utils/service_locator.dart';
56
import 'package:flutter/material.dart';
@@ -20,6 +21,9 @@ class DynamicFormApp extends StatelessWidget {
2021
BlocProvider<GetFormCubit>(
2122
create: (context) => GetFormCubit(locator()),
2223
),
24+
BlocProvider<PostFormCubit>(
25+
create: (context) => PostFormCubit(locator()),
26+
),
2327
],
2428
child: MaterialApp(
2529
navigatorKey: navigatorKey,

lib/presentation/screens/form_screen.dart

+44-24
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
import 'package:dynamicform/business_logic/post_form_cubit/post_form_cubit.dart';
2+
import 'package:dynamicform/business_logic/post_form_cubit/post_form_state.dart';
13
import 'package:dynamicform/core/constants/text_constants.dart';
24
import 'package:dynamicform/core/model/form_model.dart';
35
import 'package:dynamicform/core/routes/app_routes.dart';
6+
import 'package:dynamicform/core/utils/show_flushbar.dart';
47
import 'package:dynamicform/presentation/screens/view_form_data_screen.dart';
58
import 'package:dynamicform/presentation/widgets/page_widget.dart';
69
import 'package:flutter/material.dart';
10+
import 'package:flutter_bloc/flutter_bloc.dart';
711

812
class DynamicFormScreen extends StatefulWidget {
913
final DynamicFormModel formModel;
@@ -49,26 +53,38 @@ class _DynamicFormScreenState extends State<DynamicFormScreen> {
4953
@override
5054
Widget build(BuildContext context) {
5155
DynamicFormModel formModel = widget.formModel;
52-
return Listener(
53-
onPointerDown: (event) => FocusManager.instance.primaryFocus?.unfocus(),
54-
child: GestureDetector(
55-
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
56-
child: Scaffold(
57-
resizeToAvoidBottomInset: false,
58-
floatingActionButtonLocation:
59-
FloatingActionButtonLocation.centerDocked,
60-
floatingActionButton: _buildNextFinishButton(formModel),
61-
body: PageView.builder(
62-
controller: pageController,
63-
itemCount: formModel.pages?.length ?? 0,
64-
physics: const NeverScrollableScrollPhysics(),
65-
itemBuilder: (context, index) => Form(
66-
key: pageFormKeys[index],
67-
child:
68-
DynamicPageWidget(pageFieldsData: formModel.pages![index])),
69-
onPageChanged: (newPageIndex) {
70-
pageAt.value = newPageIndex;
71-
},
56+
return BlocListener<PostFormCubit, PostFormState>(
57+
listener: (context, postFormState) {
58+
if (postFormState is PostFormSuccessState) {
59+
debugPrint("postFormSuccess: ${postFormState.postFormSuccessModel}");
60+
Navigator.of(context).pushNamedAndRemoveUntil(
61+
AppRoutes.listOfFormsScreen, (route) => false);
62+
} else if (postFormState is PostFormFailureState) {
63+
debugPrint("postFormFailure: ${postFormState.failure}");
64+
showFlushBar(context, postFormState.failure.getErrorMsg());
65+
}
66+
},
67+
child: Listener(
68+
onPointerDown: (event) => FocusManager.instance.primaryFocus?.unfocus(),
69+
child: GestureDetector(
70+
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
71+
child: Scaffold(
72+
resizeToAvoidBottomInset: false,
73+
floatingActionButtonLocation:
74+
FloatingActionButtonLocation.centerDocked,
75+
floatingActionButton: _buildNextFinishButton(formModel),
76+
body: PageView.builder(
77+
controller: pageController,
78+
itemCount: formModel.pages?.length ?? 0,
79+
physics: const NeverScrollableScrollPhysics(),
80+
itemBuilder: (context, index) => Form(
81+
key: pageFormKeys[index],
82+
child: DynamicPageWidget(
83+
pageFieldsData: formModel.pages![index])),
84+
onPageChanged: (newPageIndex) {
85+
pageAt.value = newPageIndex;
86+
},
87+
),
7288
),
7389
),
7490
),
@@ -114,12 +130,16 @@ class _DynamicFormScreenState extends State<DynamicFormScreen> {
114130
field.value;
115131
}
116132

117-
// store the filled values in
118133
if (isAtLastPage(pageAt: pageIndex)) {
119134
debugPrint("page validated and at last page");
120-
Navigator.of(context).pushNamedAndRemoveUntil(
121-
AppRoutes.listOfFormsScreen,
122-
(route) => false);
135+
BlocProvider.of<PostFormCubit>(context)
136+
.postForm(
137+
formData: ViewFormDataScreen
138+
.dynamicFormFilledValues,
139+
);
140+
// Navigator.of(context).pushNamedAndRemoveUntil(
141+
// AppRoutes.listOfFormsScreen,
142+
// (route) => false);
123143
} else {
124144
goNextPage();
125145
}

lib/presentation/screens/list_of_forms_screen.dart

+73-58
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import 'package:dynamicform/business_logic/get_forms_cubit/get_forms_cubit.dart';
22
import 'package:dynamicform/business_logic/get_forms_cubit/get_forms_state.dart';
3+
import 'package:dynamicform/business_logic/post_form_cubit/post_form_cubit.dart';
4+
import 'package:dynamicform/business_logic/post_form_cubit/post_form_state.dart';
35
import 'package:dynamicform/core/constants/text_constants.dart';
46
import 'package:dynamicform/core/model/form_model.dart';
57
import 'package:dynamicform/core/routes/app_routes.dart';
8+
import 'package:dynamicform/core/utils/show_flushbar.dart';
69
import 'package:dynamicform/presentation/widgets/custom_text_widget.dart';
710
import 'package:flutter/material.dart';
811
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -27,67 +30,79 @@ class _ListOfFormsScreenState extends State<ListOfFormsScreen> {
2730

2831
@override
2932
Widget build(BuildContext context) {
30-
return Scaffold(
31-
appBar: AppBar(
32-
title: const CustomText(text: TextConstants.formTemplates),
33-
),
34-
body: RefreshIndicator(
35-
onRefresh: _onRefresh,
36-
child: BlocBuilder<GetFormsCubit, GetFormsState>(
37-
builder: (context, state) {
38-
if (state is GetFormsLoadingState) {
39-
return const Center(child: CircularProgressIndicator());
40-
} else if (state is GetFormsFailureState) {
41-
return Center(
42-
child: SingleChildScrollView(
43-
physics: const AlwaysScrollableScrollPhysics(),
44-
child: Container(
45-
height: MediaQuery.of(context).size.height - 100,
46-
alignment: Alignment.center,
47-
child: CustomText(text: state.failure.getErrorMsg()),
33+
return BlocListener<PostFormCubit, PostFormState>(
34+
listener: (context, postFormState) {
35+
if (postFormState is PostFormSuccessState) {
36+
showFlushBar(
37+
context,
38+
postFormState.postFormSuccessModel.message ?? TextConstants.nA,
39+
);
40+
}
41+
},
42+
child: Scaffold(
43+
appBar: AppBar(
44+
title: const CustomText(text: TextConstants.formTemplates),
45+
),
46+
body: RefreshIndicator(
47+
onRefresh: _onRefresh,
48+
child: BlocBuilder<GetFormsCubit, GetFormsState>(
49+
builder: (context, state) {
50+
if (state is GetFormsLoadingState) {
51+
return const Center(child: CircularProgressIndicator());
52+
} else if (state is GetFormsFailureState) {
53+
return Center(
54+
child: SingleChildScrollView(
55+
physics: const AlwaysScrollableScrollPhysics(),
56+
child: Container(
57+
height: MediaQuery.of(context).size.height - 100,
58+
alignment: Alignment.center,
59+
child: CustomText(text: state.failure.getErrorMsg()),
60+
),
4861
),
62+
);
63+
} else if (state is GetFormsSuccessState) {
64+
List<DynamicFormModel> forms = state.forms.forms ?? [];
65+
return forms.isEmpty
66+
? const Center(
67+
child: CustomText(
68+
text: TextConstants.noFormsAvailable,
69+
),
70+
)
71+
: ListView.builder(
72+
physics: const AlwaysScrollableScrollPhysics(),
73+
itemCount: forms.length,
74+
itemBuilder: (context, index) {
75+
return ListTile(
76+
title: CustomText(
77+
text: forms[index].formName ?? TextConstants.nA,
78+
),
79+
leading: CustomText(
80+
text: "${index + 1}.",
81+
),
82+
onTap: () {
83+
BlocProvider.of<PostFormCubit>(context)
84+
.formIdInDB = forms[index].formId;
85+
Navigator.pushNamed(
86+
context, AppRoutes.fillFormScreen,
87+
arguments: forms[index]);
88+
},
89+
trailing:
90+
const Icon(Icons.arrow_forward_ios, size: 18),
91+
);
92+
},
93+
);
94+
}
95+
96+
return SingleChildScrollView(
97+
physics: const AlwaysScrollableScrollPhysics(),
98+
child: Container(
99+
height: MediaQuery.of(context).size.height - 100,
100+
alignment: Alignment.center,
101+
child: const CustomText(text: TextConstants.noFormsAvailable),
49102
),
50103
);
51-
} else if (state is GetFormsSuccessState) {
52-
List<DynamicFormModel> forms = state.forms.forms ?? [];
53-
return forms.isEmpty
54-
? const Center(
55-
child: CustomText(
56-
text: TextConstants.noFormsAvailable,
57-
),
58-
)
59-
: ListView.builder(
60-
physics: const AlwaysScrollableScrollPhysics(),
61-
itemCount: forms.length,
62-
itemBuilder: (context, index) {
63-
return ListTile(
64-
title: CustomText(
65-
text: forms[index].formName ?? TextConstants.nA,
66-
),
67-
leading: CustomText(
68-
text: "${index + 1}.",
69-
),
70-
onTap: () {
71-
Navigator.pushNamed(
72-
context, AppRoutes.fillFormScreen,
73-
arguments: forms[index]);
74-
},
75-
trailing:
76-
const Icon(Icons.arrow_forward_ios, size: 18),
77-
);
78-
},
79-
);
80-
}
81-
82-
return SingleChildScrollView(
83-
physics: const AlwaysScrollableScrollPhysics(),
84-
child: Container(
85-
height: MediaQuery.of(context).size.height - 100,
86-
alignment: Alignment.center,
87-
child: const CustomText(text: TextConstants.noFormsAvailable),
88-
),
89-
);
90-
},
104+
},
105+
),
91106
),
92107
),
93108
);

0 commit comments

Comments
 (0)