Merge branch 'feedback' into 'master'
Refactored code to use CustomSnackBar instead of ScaffoldMessenger for... See merge request profile-image/kedaireka/polinema-adapative-learning/mobile-adaptive-learning!11
This commit is contained in:
commit
eb18fe4bac
|
|
@ -1,2 +1 @@
|
|||
const String baseUrl =
|
||||
'https://4317-2001-448a-50a0-3463-1809-e54b-6523-d37.ngrok-free.app/';
|
||||
const String baseUrl = 'https://ea80-114-6-25-184.ngrok-free.app/';
|
||||
|
|
|
|||
56
lib/core/widgets/custom_snackbar.dart
Normal file
56
lib/core/widgets/custom_snackbar.dart
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomSnackBar {
|
||||
static void show(
|
||||
BuildContext context, {
|
||||
required String message,
|
||||
bool isError = false,
|
||||
Duration duration = const Duration(seconds: 3),
|
||||
}) {
|
||||
ScaffoldMessenger.of(context).clearSnackBars();
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
message,
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
backgroundColor: isError ? Colors.red : Colors.green,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
margin: const EdgeInsets.all(16),
|
||||
duration: duration,
|
||||
dismissDirection: DismissDirection.horizontal,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Versi dengan custom styling
|
||||
static void showCustom(
|
||||
BuildContext context, {
|
||||
required String message,
|
||||
Color? backgroundColor,
|
||||
Color? textColor,
|
||||
Duration duration = const Duration(seconds: 3),
|
||||
SnackBarBehavior behavior = SnackBarBehavior.floating,
|
||||
}) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
message,
|
||||
style: TextStyle(color: textColor ?? Colors.white),
|
||||
),
|
||||
backgroundColor: backgroundColor ?? Colors.black,
|
||||
behavior: behavior,
|
||||
margin: const EdgeInsets.all(16),
|
||||
duration: duration,
|
||||
dismissDirection: DismissDirection.horizontal,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:english_learning/core/widgets/custom_snackbar.dart';
|
||||
import 'package:english_learning/features/auth/provider/user_provider.dart';
|
||||
import 'package:english_learning/features/auth/provider/validator_provider.dart';
|
||||
import 'package:english_learning/features/auth/screens/signin/signin_screen.dart';
|
||||
|
|
@ -50,11 +51,10 @@ class _ForgotPasswordScreenState extends State<ForgotPasswordScreen> {
|
|||
},
|
||||
);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Email is not registered!'),
|
||||
backgroundColor: AppColors.redColor,
|
||||
),
|
||||
CustomSnackBar.show(
|
||||
context,
|
||||
message: 'Email is not registered!',
|
||||
isError: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:english_learning/core/widgets/custom_snackbar.dart';
|
||||
import 'package:english_learning/features/auth/provider/user_provider.dart';
|
||||
import 'package:english_learning/features/auth/provider/validator_provider.dart';
|
||||
import 'package:english_learning/features/auth/screens/forgot_password/forgot_password_screen.dart';
|
||||
|
|
@ -169,13 +170,11 @@ class SigninScreen extends StatelessWidget {
|
|||
_passwordController.clear();
|
||||
});
|
||||
} else {
|
||||
// Show error message
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text(
|
||||
'Login failed, please check your credentials'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
CustomSnackBar.show(
|
||||
context,
|
||||
message:
|
||||
'Login failed, please check your credentials',
|
||||
isError: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:english_learning/core/widgets/custom_snackbar.dart';
|
||||
import 'package:english_learning/features/auth/provider/user_provider.dart';
|
||||
import 'package:english_learning/features/auth/provider/validator_provider.dart';
|
||||
import 'package:english_learning/features/auth/screens/signin/signin_screen.dart';
|
||||
|
|
@ -71,11 +72,10 @@ class _SignupScreenState extends State<SignupScreen> {
|
|||
);
|
||||
} else {
|
||||
// Show error message
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Registration failed'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
CustomSnackBar.show(
|
||||
context,
|
||||
message: 'Registration failed',
|
||||
isError: true,
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:english_learning/core/utils/styles/theme.dart';
|
||||
import 'package:english_learning/core/widgets/custom_snackbar.dart';
|
||||
import 'package:english_learning/core/widgets/global_button.dart';
|
||||
import 'package:english_learning/features/auth/provider/user_provider.dart';
|
||||
import 'package:english_learning/features/learning/modules/exercises/widgets/complete_submission.dart';
|
||||
|
|
@ -56,8 +57,10 @@ class ExerciseNavigator extends StatelessWidget {
|
|||
} catch (e) {
|
||||
print('Error submitting answers: $e');
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Error: $e')),
|
||||
CustomSnackBar.show(
|
||||
context,
|
||||
message: 'Error submitting answers: $e',
|
||||
isError: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -139,12 +142,11 @@ class ExerciseNavigator extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text(
|
||||
CustomSnackBar.show(
|
||||
context,
|
||||
message:
|
||||
'Please answer at least one question before submitting.',
|
||||
),
|
||||
),
|
||||
isError: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:english_learning/core/widgets/custom_snackbar.dart';
|
||||
import 'package:english_learning/features/learning/modules/exercises/models/review_exercise_model.dart';
|
||||
import 'package:english_learning/features/learning/modules/exercises/providers/exercise_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -166,11 +167,10 @@ class MatchingPairsQuestion extends StatelessWidget {
|
|||
? null
|
||||
: () {
|
||||
if (!isLeft && provider.activeLeftOption == null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Please select a left option first.'),
|
||||
duration: Duration(seconds: 1),
|
||||
),
|
||||
CustomSnackBar.show(
|
||||
context,
|
||||
message: 'Please select a left option first.',
|
||||
isError: true,
|
||||
);
|
||||
} else {
|
||||
provider.answerQuestion(exerciseIndex, option);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:english_learning/core/widgets/custom_snackbar.dart';
|
||||
import 'package:english_learning/core/widgets/global_button.dart';
|
||||
import 'package:english_learning/core/utils/styles/theme.dart';
|
||||
import 'package:english_learning/features/learning/modules/exercises/providers/exercise_provider.dart';
|
||||
|
|
@ -25,6 +26,7 @@ class FeedbackScreen extends StatefulWidget {
|
|||
class _FeedbackScreenState extends State<FeedbackScreen> {
|
||||
final FocusNode _focusNode = FocusNode();
|
||||
final TextEditingController _controller = TextEditingController();
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -45,42 +47,10 @@ class _FeedbackScreenState extends State<FeedbackScreen> {
|
|||
_focusNode.unfocus();
|
||||
}
|
||||
|
||||
Future<void> _submitFeedback() async {
|
||||
if (_controller.text.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Please enter your feedback before submitting.'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
Future<void> _navigateToLevelList() async {
|
||||
if (!mounted) return;
|
||||
|
||||
final exerciseProvider =
|
||||
Provider.of<ExerciseProvider>(context, listen: false);
|
||||
|
||||
try {
|
||||
final result = await exerciseProvider.submitFeedback(_controller.text);
|
||||
|
||||
print('Feedback submitted successfully: $result');
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Feedback submitted successfully!'),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
|
||||
// Show the dialog
|
||||
if (mounted) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext dialogContext) {
|
||||
return FeedbackDialog(
|
||||
onSubmit: () {
|
||||
Navigator.of(dialogContext).pop(); // Close the dialog
|
||||
Navigator.pushAndRemoveUntil(
|
||||
await Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => LevelListScreen(
|
||||
|
|
@ -88,20 +58,71 @@ class _FeedbackScreenState extends State<FeedbackScreen> {
|
|||
topicTitle: widget.topicTitle,
|
||||
),
|
||||
),
|
||||
(route) => false, // This removes all previous routes
|
||||
(Route<dynamic> route) => route.isFirst,
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
Future<void> _submitFeedback() async {
|
||||
_unfocusTextField();
|
||||
|
||||
if (_controller.text.trim().isEmpty) {
|
||||
CustomSnackBar.show(
|
||||
context,
|
||||
message: 'Please enter your feedback before submitting.',
|
||||
isError: true,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
final exerciseProvider =
|
||||
Provider.of<ExerciseProvider>(context, listen: false);
|
||||
final result =
|
||||
await exerciseProvider.submitFeedback(_controller.text.trim());
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
|
||||
if (result != null) {
|
||||
await showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext dialogContext) => PopScope(
|
||||
canPop: false,
|
||||
child: FeedbackDialog(
|
||||
onSubmit: () {
|
||||
Navigator.pop(dialogContext);
|
||||
_navigateToLevelList();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
CustomSnackBar.show(
|
||||
context,
|
||||
message: 'Failed to submit feedback. Please try again.',
|
||||
isError: true,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error submitting feedback: $e');
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Failed to submit feedback: $e'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
|
||||
CustomSnackBar.show(
|
||||
context,
|
||||
message: 'An error occurred: ${e.toString()}',
|
||||
isError: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -129,9 +150,11 @@ class _FeedbackScreenState extends State<FeedbackScreen> {
|
|||
),
|
||||
),
|
||||
),
|
||||
body: Padding(
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
|
|
@ -148,7 +171,7 @@ class _FeedbackScreenState extends State<FeedbackScreen> {
|
|||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: _focusNode.hasFocus
|
||||
? AppColors.blueColor.withOpacity(0.3)
|
||||
? AppColors.blueColor.withOpacity(0.2)
|
||||
: Colors.transparent,
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 4),
|
||||
|
|
@ -168,9 +191,7 @@ class _FeedbackScreenState extends State<FeedbackScreen> {
|
|||
hintStyle:
|
||||
AppTextStyles.greyTextStyle.copyWith(fontSize: 14),
|
||||
filled: true,
|
||||
fillColor: _focusNode.hasFocus
|
||||
? AppColors.whiteColor
|
||||
: AppColors.whiteColor,
|
||||
fillColor: AppColors.whiteColor,
|
||||
contentPadding: const EdgeInsets.all(16),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
|
|
@ -185,8 +206,9 @@ class _FeedbackScreenState extends State<FeedbackScreen> {
|
|||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(
|
||||
color: AppColors.disableColor.withOpacity(0.5)),
|
||||
borderSide: const BorderSide(
|
||||
color: AppColors.disableColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -194,7 +216,8 @@ class _FeedbackScreenState extends State<FeedbackScreen> {
|
|||
const SizedBox(height: 24),
|
||||
GlobalButton(
|
||||
text: 'Send',
|
||||
onPressed: _submitFeedback,
|
||||
isLoading: _isLoading,
|
||||
onPressed: _isLoading ? null : _submitFeedback,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
GlobalButton(
|
||||
|
|
@ -202,22 +225,14 @@ class _FeedbackScreenState extends State<FeedbackScreen> {
|
|||
textColor: AppColors.blueColor,
|
||||
backgroundColor: Colors.transparent,
|
||||
borderColor: AppColors.blueColor,
|
||||
onPressed: () {
|
||||
Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => LevelListScreen(
|
||||
topicId: widget.topicId,
|
||||
topicTitle: widget.topicTitle,
|
||||
onPressed: _isLoading ? null : _navigateToLevelList,
|
||||
),
|
||||
),
|
||||
(Route<dynamic> route) => route.isFirst);
|
||||
},
|
||||
)
|
||||
const SizedBox(height: 24),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@ class FeedbackDialog extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Dialog(
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
child: Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
|
|
@ -41,7 +43,7 @@ class FeedbackDialog extends StatelessWidget {
|
|||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
'Thank you for for taking the time to share your perspective. Your feedback is highly valued!',
|
||||
'Thank you for taking the time to share your perspective. Your feedback is highly valued!',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppTextStyles.disableTextStyle.copyWith(
|
||||
fontSize: 12,
|
||||
|
|
@ -51,24 +53,13 @@ class FeedbackDialog extends StatelessWidget {
|
|||
const SizedBox(height: 30),
|
||||
GlobalButton(
|
||||
text: 'Got It',
|
||||
onPressed: () {
|
||||
// Navigator.push(
|
||||
// context,
|
||||
// MaterialPageRoute(
|
||||
// builder: (context) => const LevelListScreen(),
|
||||
// ),
|
||||
// );
|
||||
onSubmit();
|
||||
// Navigator.of(context).popUntil(
|
||||
// (route) => route.isFirst,
|
||||
// );
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
)
|
||||
onPressed: onSubmit,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:english_learning/core/services/dio_client.dart';
|
||||
import 'package:english_learning/core/services/repositories/student_learning_repository.dart';
|
||||
import 'package:english_learning/core/widgets/custom_snackbar.dart';
|
||||
import 'package:english_learning/core/widgets/global_button.dart';
|
||||
import 'package:english_learning/features/auth/provider/user_provider.dart';
|
||||
import 'package:english_learning/features/learning/modules/exercises/screens/exercise_screen.dart';
|
||||
|
|
@ -86,9 +87,7 @@ class _MaterialScreenState extends State<MaterialScreen>
|
|||
await _repository.createStudentLearning(widget.levelId, token);
|
||||
_navigateToExercise(result['payload']['ID_STUDENT_LEARNING']);
|
||||
} catch (e) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Error: $e')),
|
||||
);
|
||||
CustomSnackBar.show(context, message: 'Error: $e', isError: true);
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:english_learning/core/widgets/custom_snackbar.dart';
|
||||
import 'package:english_learning/features/auth/provider/user_provider.dart';
|
||||
import 'package:english_learning/features/auth/provider/validator_provider.dart';
|
||||
import 'package:english_learning/core/widgets/form_field/custom_field_widget.dart';
|
||||
|
|
@ -121,10 +122,11 @@ class _ChangePasswordScreenState extends State<ChangePasswordScreen> {
|
|||
},
|
||||
);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
'Failed to update password. Please try again.')),
|
||||
CustomSnackBar.show(
|
||||
context,
|
||||
message:
|
||||
'Failed to update password. Please try again.',
|
||||
isError: true,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:io';
|
||||
import 'package:english_learning/core/services/constants.dart';
|
||||
import 'package:english_learning/core/utils/styles/theme.dart';
|
||||
import 'package:english_learning/core/widgets/custom_snackbar.dart';
|
||||
import 'package:english_learning/core/widgets/form_field/custom_field_widget.dart';
|
||||
import 'package:english_learning/core/widgets/global_button.dart';
|
||||
import 'package:english_learning/features/auth/provider/user_provider.dart';
|
||||
|
|
@ -76,10 +77,9 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
|
|||
},
|
||||
);
|
||||
} catch (e) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Failed to update profile. Please try again.')),
|
||||
);
|
||||
CustomSnackBar.show(context,
|
||||
message: 'Failed to update profile. Please try again.',
|
||||
isError: true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:english_learning/core/utils/styles/theme.dart';
|
||||
import 'package:english_learning/core/widgets/custom_snackbar.dart';
|
||||
import 'package:english_learning/core/widgets/global_button.dart';
|
||||
import 'package:english_learning/features/auth/provider/user_provider.dart';
|
||||
import 'package:english_learning/features/settings/modules/report_issue/widgets/report_issue_dialog.dart';
|
||||
|
|
@ -61,10 +62,8 @@ class _ReportIssueScreenState extends State<ReportIssueScreen> {
|
|||
},
|
||||
);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Failed to submit report. Please try again.')),
|
||||
);
|
||||
CustomSnackBar.show(context,
|
||||
message: 'Failed to submit report. Please try again.', isError: true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:bootstrap_icons/bootstrap_icons.dart';
|
||||
import 'package:english_learning/core/services/constants.dart';
|
||||
import 'package:english_learning/core/widgets/custom_snackbar.dart';
|
||||
import 'package:english_learning/features/auth/provider/user_provider.dart';
|
||||
import 'package:english_learning/features/auth/screens/signin/signin_screen.dart';
|
||||
import 'package:english_learning/features/settings/modules/change_password/screens/change_password_screen.dart';
|
||||
|
|
@ -181,10 +182,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
(Route<dynamic> route) => false,
|
||||
);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text(
|
||||
'Logout failed. Please try again.')),
|
||||
CustomSnackBar.show(
|
||||
context,
|
||||
message: 'Logout failed. Please try again.',
|
||||
isError: true,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user