refactor: adjust spacing and improve layout across multiple screens
This commit is contained in:
parent
bbd26c0cb1
commit
aae59d0a86
|
|
@ -21,6 +21,9 @@ class CustomButton extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final mediaQuery = MediaQuery.of(context);
|
||||||
|
final screenWidth = mediaQuery.size.width;
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
|
|
@ -46,7 +49,7 @@ class CustomButton extends StatelessWidget {
|
||||||
text,
|
text,
|
||||||
style: textStyle ??
|
style: textStyle ??
|
||||||
AppTextStyles.blackButtonTextStyle.copyWith(
|
AppTextStyles.blackButtonTextStyle.copyWith(
|
||||||
fontSize: 14,
|
fontSize: screenWidth * 0.036,
|
||||||
fontWeight: FontWeight.w900,
|
fontWeight: FontWeight.w900,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -50,20 +50,23 @@ class CustomFieldWidget extends StatefulWidget {
|
||||||
|
|
||||||
class _CustomFieldWidgetState extends State<CustomFieldWidget> {
|
class _CustomFieldWidgetState extends State<CustomFieldWidget> {
|
||||||
late TextEditingController _controller;
|
late TextEditingController _controller;
|
||||||
|
late ValidatorProvider _validatorProvider;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_controller = widget.controller ?? TextEditingController();
|
_controller = widget.controller ?? TextEditingController();
|
||||||
|
|
||||||
context
|
// Simpan referensi provider di initState
|
||||||
.read<ValidatorProvider>()
|
_validatorProvider = context.read<ValidatorProvider>();
|
||||||
.setController(widget.fieldName, _controller);
|
_validatorProvider.setController(widget.fieldName, _controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
context.read<ValidatorProvider>().removeController(widget.fieldName);
|
// Gunakan referensi yang disimpan sebelumnya
|
||||||
|
_validatorProvider.removeController(widget.fieldName);
|
||||||
|
|
||||||
if (widget.controller == null) {
|
if (widget.controller == null) {
|
||||||
_controller.dispose();
|
_controller.dispose();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,7 @@ class _ForgotPasswordScreenState extends State<ForgotPasswordScreen> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
SizedBox(height: screenHeight * 0.1)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,9 @@ class SigninScreen extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SigninScreenState extends State<SigninScreen> {
|
class _SigninScreenState extends State<SigninScreen> {
|
||||||
final TextEditingController _loginController = TextEditingController();
|
late TextEditingController _loginController = TextEditingController();
|
||||||
final TextEditingController _passwordController = TextEditingController();
|
late TextEditingController _passwordController = TextEditingController();
|
||||||
|
late ValidatorProvider _validatorProvider;
|
||||||
final FocusNode _loginFocus = FocusNode();
|
final FocusNode _loginFocus = FocusNode();
|
||||||
final FocusNode _passwordFocus = FocusNode();
|
final FocusNode _passwordFocus = FocusNode();
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
|
@ -28,16 +29,22 @@ class _SigninScreenState extends State<SigninScreen> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
context.read<ValidatorProvider>().setController('login', _loginController);
|
_loginController = TextEditingController();
|
||||||
context
|
_passwordController = TextEditingController();
|
||||||
.read<ValidatorProvider>()
|
}
|
||||||
.setController('password', _passwordController);
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
_validatorProvider = context.read<ValidatorProvider>();
|
||||||
|
_validatorProvider.setController('login', _loginController);
|
||||||
|
_validatorProvider.setController('password', _passwordController);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
context.read<ValidatorProvider>().removeController('login');
|
_validatorProvider.removeController('login');
|
||||||
context.read<ValidatorProvider>().removeController('password');
|
_validatorProvider.removeController('password');
|
||||||
|
|
||||||
_loginController.dispose();
|
_loginController.dispose();
|
||||||
_passwordController.dispose();
|
_passwordController.dispose();
|
||||||
|
|
@ -346,7 +353,7 @@ class _SigninScreenState extends State<SigninScreen> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: screenHeight * 0.02),
|
SizedBox(height: screenHeight * 0.1),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ class LoginEmailField extends StatefulWidget {
|
||||||
const LoginEmailField({super.key});
|
const LoginEmailField({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_LoginEmailFieldState createState() => _LoginEmailFieldState();
|
LoginEmailFieldState createState() => LoginEmailFieldState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LoginEmailFieldState extends State<LoginEmailField> {
|
class LoginEmailFieldState extends State<LoginEmailField> {
|
||||||
late FocusNode _focusNode;
|
late FocusNode _focusNode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
||||||
|
|
@ -316,6 +316,7 @@ class _SignupScreenState extends State<SignupScreen> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
SizedBox(height: screenHeight * 0.1)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -67,14 +67,18 @@ class HistoryProvider with ChangeNotifier {
|
||||||
Future<void> fetchLearningHistory(String token) async {
|
Future<void> fetchLearningHistory(String token) async {
|
||||||
if (_sectionProvider.sections.isEmpty) {
|
if (_sectionProvider.sections.isEmpty) {
|
||||||
_error = 'No sections available';
|
_error = 'No sections available';
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String sectionId = _sectionProvider.sections[_selectedPageIndex].id;
|
String sectionId = _sectionProvider.sections[_selectedPageIndex].id;
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
_error = null;
|
_error = null;
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final history = await _repository.getLearningHistory(sectionId, token);
|
final history = await _repository.getLearningHistory(sectionId, token);
|
||||||
|
|
@ -85,7 +89,9 @@ class HistoryProvider with ChangeNotifier {
|
||||||
_historyModel = [];
|
_historyModel = [];
|
||||||
} finally {
|
} finally {
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -361,6 +361,9 @@ class _HomeContentState extends State<HomeContent> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final mediaQuery = MediaQuery.of(context);
|
||||||
|
final screenHeight = mediaQuery.size.height;
|
||||||
|
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: _initializeData,
|
onRefresh: _initializeData,
|
||||||
child: Consumer2<UserProvider, CompletedTopicsProvider>(
|
child: Consumer2<UserProvider, CompletedTopicsProvider>(
|
||||||
|
|
@ -486,7 +489,7 @@ class _HomeContentState extends State<HomeContent> {
|
||||||
return WelcomeCard(cardModel: cardData.cardData[index]);
|
return WelcomeCard(cardModel: cardData.cardData[index]);
|
||||||
},
|
},
|
||||||
options: CarouselOptions(
|
options: CarouselOptions(
|
||||||
height: 168,
|
height: screenHeight * 0.19,
|
||||||
viewportFraction: 0.9,
|
viewportFraction: 0.9,
|
||||||
enlargeCenterPage: true,
|
enlargeCenterPage: true,
|
||||||
autoPlay: true,
|
autoPlay: true,
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ class IncompleteSubmission extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: GlobalButton(
|
child: GlobalButton(
|
||||||
text: 'Check Again',
|
text: 'Check',
|
||||||
textColor: AppColors.blueColor,
|
textColor: AppColors.blueColor,
|
||||||
borderColor: AppColors.blueColor,
|
borderColor: AppColors.blueColor,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:english_learning/core/services/repositories/level_repository.dart';
|
import 'package:english_learning/core/services/repositories/level_repository.dart';
|
||||||
import 'package:english_learning/features/learning/modules/level/models/level_model.dart';
|
import 'package:english_learning/features/learning/modules/level/models/level_model.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
class LevelProvider with ChangeNotifier {
|
class LevelProvider with ChangeNotifier {
|
||||||
final LevelRepository _levelRepository = LevelRepository();
|
final LevelRepository _levelRepository = LevelRepository();
|
||||||
|
|
@ -21,8 +21,9 @@ class LevelProvider with ChangeNotifier {
|
||||||
Future<void> fetchLevels(String topicId, String token) async {
|
Future<void> fetchLevels(String topicId, String token) async {
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
_error = null;
|
_error = null;
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
final result = await _levelRepository.getLevels(topicId, token);
|
final result = await _levelRepository.getLevels(topicId, token);
|
||||||
_levels = result['levels'];
|
_levels = result['levels'];
|
||||||
|
|
@ -37,7 +38,9 @@ class LevelProvider with ChangeNotifier {
|
||||||
_error = 'Error fetching levels: ${e.toString()}';
|
_error = 'Error fetching levels: ${e.toString()}';
|
||||||
} finally {
|
} finally {
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,18 @@ class LevelCard extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final mediaQuery = MediaQuery.of(context);
|
||||||
|
final screenHeight = mediaQuery.size.height;
|
||||||
|
|
||||||
return Card(
|
return Card(
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
child: Container(
|
child: Container(
|
||||||
height: MediaQuery.of(context).size.height * 0.6,
|
constraints: BoxConstraints(
|
||||||
|
maxHeight: screenHeight * 0.7,
|
||||||
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
gradient: isAllowed
|
gradient: isAllowed
|
||||||
|
|
@ -107,20 +112,20 @@ class LevelCard extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
// Score Display
|
|
||||||
Text(
|
Text(
|
||||||
// 'Score ${level.score}/100',
|
|
||||||
'Score $score/100',
|
'Score $score/100',
|
||||||
style: AppTextStyles.whiteTextStyle.copyWith(
|
style: AppTextStyles.whiteTextStyle.copyWith(
|
||||||
fontWeight: FontWeight.w900,
|
fontWeight: FontWeight.w900,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(), // Learn Now Button
|
const Spacer(),
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8.0,
|
||||||
|
),
|
||||||
child: CustomButton(
|
child: CustomButton(
|
||||||
text: isAllowed ? 'Learn Now' : 'Locked',
|
text: isAllowed ? 'Learn Now' : 'Locked',
|
||||||
textStyle:
|
textStyle:
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,9 @@ class PretestCard extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final mediaQuery = MediaQuery.of(context);
|
||||||
|
final screenHeight = mediaQuery.size.height;
|
||||||
|
|
||||||
return Consumer<LevelProvider>(builder: (context, levelProvider, _) {
|
return Consumer<LevelProvider>(builder: (context, levelProvider, _) {
|
||||||
final isFinished = levelProvider.isPretestFinished(pretest.idLevel);
|
final isFinished = levelProvider.isPretestFinished(pretest.idLevel);
|
||||||
final score = levelProvider.getPretestScore(pretest.idLevel);
|
final score = levelProvider.getPretestScore(pretest.idLevel);
|
||||||
|
|
@ -80,7 +83,9 @@ class PretestCard extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: 4, horizontal: 8),
|
vertical: 4,
|
||||||
|
horizontal: 8,
|
||||||
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
color:
|
color:
|
||||||
|
|
@ -125,7 +130,7 @@ class PretestCard extends StatelessWidget {
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Image.asset(
|
child: Image.asset(
|
||||||
'lib/features/learning/modules/level/assets/images/pretest_level_illustration.png',
|
'lib/features/learning/modules/level/assets/images/pretest_level_illustration.png',
|
||||||
height: 95,
|
height: screenHeight * 0.13,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -5,22 +5,42 @@ import 'package:flutter/foundation.dart';
|
||||||
class SectionProvider extends ChangeNotifier {
|
class SectionProvider extends ChangeNotifier {
|
||||||
final SectionRepository _repository = SectionRepository();
|
final SectionRepository _repository = SectionRepository();
|
||||||
List<Section> _sections = [];
|
List<Section> _sections = [];
|
||||||
final bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
dynamic _error;
|
dynamic _error;
|
||||||
|
|
||||||
List<Section> get sections => _sections;
|
List<Section> get sections => _sections;
|
||||||
bool get isLoading => _isLoading;
|
bool get isLoading => _isLoading;
|
||||||
String? get error => _error;
|
String? get error => _error;
|
||||||
|
|
||||||
Future<List<Section>> fetchSections(String token) async {
|
void resetData() {
|
||||||
|
_sections = [];
|
||||||
|
_error = null;
|
||||||
|
_isLoading = true;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> fetchSections(String token) async {
|
||||||
try {
|
try {
|
||||||
|
// Set loading state
|
||||||
|
_isLoading = true;
|
||||||
|
_error = null;
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
// Fetch sections
|
||||||
_sections = await _repository.getSections(token);
|
_sections = await _repository.getSections(token);
|
||||||
|
|
||||||
|
// Reset loading state
|
||||||
|
_isLoading = false;
|
||||||
|
_error = null;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
return _sections;
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_error = e.toString();
|
// Handle error
|
||||||
|
_isLoading = false;
|
||||||
|
_error = e;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
return [];
|
|
||||||
|
// Rethrow the error to be handled by the caller
|
||||||
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,9 @@ class _LearningScreenState extends State<LearningScreen>
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
_initializeSections();
|
_initializeSections();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _initializeSections() async {
|
Future<void> _initializeSections() async {
|
||||||
|
|
@ -32,46 +34,92 @@ class _LearningScreenState extends State<LearningScreen>
|
||||||
final sectionProvider =
|
final sectionProvider =
|
||||||
Provider.of<SectionProvider>(context, listen: false);
|
Provider.of<SectionProvider>(context, listen: false);
|
||||||
|
|
||||||
// Cek apakah sections sudah ada
|
|
||||||
if (sectionProvider.sections.isEmpty) {
|
|
||||||
try {
|
try {
|
||||||
|
// Reset data sebelum fetch
|
||||||
|
sectionProvider.resetData(); // Tambahkan method ini di SectionProvider
|
||||||
|
|
||||||
final token = await userProvider.getValidToken();
|
final token = await userProvider.getValidToken();
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
await sectionProvider.fetchSections(token);
|
await sectionProvider.fetchSections(token);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
rethrow;
|
if (mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text('Failed to load data: ${e.toString()}'),
|
||||||
|
action: SnackBarAction(
|
||||||
|
label: 'Retry',
|
||||||
|
onPressed: _initializeSections,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isInitialLoading = false;
|
_isInitialLoading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
setState(() {
|
|
||||||
_isInitialLoading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _refreshSections() async {
|
// Future<void> _refreshSections() async {
|
||||||
final userProvider = Provider.of<UserProvider>(context, listen: false);
|
// final userProvider = Provider.of<UserProvider>(context, listen: false);
|
||||||
final sectionProvider =
|
// final sectionProvider =
|
||||||
Provider.of<SectionProvider>(context, listen: false);
|
// Provider.of<SectionProvider>(context, listen: false);
|
||||||
|
|
||||||
try {
|
// try {
|
||||||
final token = await userProvider.getValidToken();
|
// final token = await userProvider.getValidToken();
|
||||||
if (token != null) {
|
// if (token != null) {
|
||||||
await sectionProvider.fetchSections(token);
|
// await sectionProvider.fetchSections(token);
|
||||||
}
|
// }
|
||||||
} catch (e) {
|
// } catch (e) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
// ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
// SnackBar(
|
||||||
content: Text('Failed to refresh sections: $e'),
|
// content: Text('Failed to refresh sections: $e'),
|
||||||
backgroundColor: Colors.red,
|
// backgroundColor: Colors.red,
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
Widget _buildErrorWidget(String error) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.error_outline,
|
||||||
|
size: 48,
|
||||||
|
color: Colors.red[300],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'Oops! Something went wrong',
|
||||||
|
style: AppTextStyles.blackTextStyle.copyWith(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
error,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: AppTextStyles.disableTextStyle.copyWith(fontSize: 14),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
ElevatedButton.icon(
|
||||||
|
onPressed: _initializeSections,
|
||||||
|
icon: const Icon(Icons.refresh),
|
||||||
|
label: const Text('Retry'),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: AppColors.primaryColor,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -115,35 +163,13 @@ class _LearningScreenState extends State<LearningScreen>
|
||||||
|
|
||||||
// Tampilkan error jika ada
|
// Tampilkan error jika ada
|
||||||
if (sectionProvider.error != null) {
|
if (sectionProvider.error != null) {
|
||||||
return RefreshIndicator(
|
return _buildErrorWidget(sectionProvider.error!);
|
||||||
onRefresh: _refreshSections,
|
|
||||||
child: ListView(
|
|
||||||
children: [
|
|
||||||
Center(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Error: ${sectionProvider.error}',
|
|
||||||
style: AppTextStyles.greyTextStyle,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: _refreshSections,
|
|
||||||
child: const Text('Retry'),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tampilkan sections atau pesan jika kosong
|
// Tampilkan sections atau pesan jika kosong
|
||||||
if (sectionProvider.sections.isEmpty) {
|
if (sectionProvider.sections.isEmpty) {
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: _refreshSections,
|
onRefresh: _initializeSections,
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: [
|
||||||
Center(
|
Center(
|
||||||
|
|
@ -165,7 +191,7 @@ class _LearningScreenState extends State<LearningScreen>
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: _refreshSections,
|
onPressed: _initializeSections,
|
||||||
child: const Text('Refresh'),
|
child: const Text('Refresh'),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|
@ -178,7 +204,7 @@ class _LearningScreenState extends State<LearningScreen>
|
||||||
|
|
||||||
// Tampilkan daftar sections
|
// Tampilkan daftar sections
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: _refreshSections,
|
onRefresh: _initializeSections,
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
itemCount: sectionProvider.sections.length,
|
itemCount: sectionProvider.sections.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ class _OnBoardingScreenState extends State<OnBoardingScreen> {
|
||||||
currentPage: _currentPage,
|
currentPage: _currentPage,
|
||||||
itemCount: controller.onBoardingData.length,
|
itemCount: controller.onBoardingData.length,
|
||||||
),
|
),
|
||||||
SizedBox(height: screenHeight * 0.2),
|
SizedBox(height: screenHeight * 0.08),
|
||||||
if (_currentPage == controller.onBoardingData.length - 1)
|
if (_currentPage == controller.onBoardingData.length - 1)
|
||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
|
|
|
||||||
|
|
@ -51,10 +51,12 @@ class _ChangePasswordScreenState extends State<ChangePasswordScreen> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(child: Consumer<ValidatorProvider>(
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
builder: (
|
||||||
child: Consumer<ValidatorProvider>(
|
context,
|
||||||
builder: (context, validatorProvider, child) {
|
validatorProvider,
|
||||||
|
child,
|
||||||
|
) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 16.0,
|
horizontal: 16.0,
|
||||||
|
|
@ -114,8 +116,7 @@ class _ChangePasswordScreenState extends State<ChangePasswordScreen> {
|
||||||
Navigator.pushAndRemoveUntil(
|
Navigator.pushAndRemoveUntil(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) =>
|
builder: (context) => const SigninScreen()),
|
||||||
const SigninScreen()),
|
|
||||||
(route) => false,
|
(route) => false,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -125,8 +126,7 @@ class _ChangePasswordScreenState extends State<ChangePasswordScreen> {
|
||||||
} else {
|
} else {
|
||||||
CustomSnackBar.show(
|
CustomSnackBar.show(
|
||||||
context,
|
context,
|
||||||
message:
|
message: 'Failed to update password. Please try again.',
|
||||||
'Failed to update password. Please try again.',
|
|
||||||
isError: true,
|
isError: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,9 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final mediaQuery = MediaQuery.of(context);
|
||||||
|
final screenHeight = mediaQuery.size.height;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
|
|
@ -196,6 +199,7 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
SizedBox(height: screenHeight * 0.05),
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ class LogoutConfirmation extends StatelessWidget {
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: GlobalButton(
|
child: GlobalButton(
|
||||||
text: 'Yes, logout!',
|
text: 'Yes',
|
||||||
onPressed: onSubmit,
|
onPressed: onSubmit,
|
||||||
textColor: AppColors.whiteColor,
|
textColor: AppColors.whiteColor,
|
||||||
backgroundColor: AppColors.redColor,
|
backgroundColor: AppColors.redColor,
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,9 @@ class SettingsScreen extends StatefulWidget {
|
||||||
class _SettingsScreenState extends State<SettingsScreen> {
|
class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final mediaQuery = MediaQuery.of(context);
|
||||||
|
final screenHeight = mediaQuery.size.height;
|
||||||
|
final screenWidth = mediaQuery.size.width;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: AppColors.bgSoftColor,
|
backgroundColor: AppColors.bgSoftColor,
|
||||||
body: Consumer<UserProvider>(builder: (context, userProvider, child) {
|
body: Consumer<UserProvider>(builder: (context, userProvider, child) {
|
||||||
|
|
@ -35,7 +38,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 210,
|
height: screenHeight * 0.33,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: AppColors.gradientTheme,
|
gradient: AppColors.gradientTheme,
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
|
|
@ -44,8 +47,12 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
top: 71.0, left: 26, right: 16.0, bottom: 19.0),
|
top: screenHeight * 0.1,
|
||||||
|
left: screenWidth * 0.07,
|
||||||
|
right: screenWidth * 0.04,
|
||||||
|
bottom: screenHeight * 0.03,
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -54,7 +61,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
UserAvatar(
|
UserAvatar(
|
||||||
radius: 60,
|
radius: screenWidth * 0.15,
|
||||||
pictureUrl: userProvider.userData?['PICTURE'],
|
pictureUrl: userProvider.userData?['PICTURE'],
|
||||||
baseUrl: '${mediaUrl}avatar/',
|
baseUrl: '${mediaUrl}avatar/',
|
||||||
onImageSelected: (File image) {
|
onImageSelected: (File image) {
|
||||||
|
|
@ -63,7 +70,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
selectedImage: userProvider.selectedImage,
|
selectedImage: userProvider.selectedImage,
|
||||||
isLoading: userProvider.isLoading,
|
isLoading: userProvider.isLoading,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 28),
|
const SizedBox(width: 16),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -72,7 +79,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
'Loading...',
|
'Loading...',
|
||||||
style:
|
style:
|
||||||
AppTextStyles.whiteTextStyle.copyWith(
|
AppTextStyles.whiteTextStyle.copyWith(
|
||||||
fontSize: 18,
|
fontSize: screenWidth * 0.05,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -82,9 +89,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
'Loading...',
|
'Loading...',
|
||||||
style:
|
style:
|
||||||
AppTextStyles.whiteTextStyle.copyWith(
|
AppTextStyles.whiteTextStyle.copyWith(
|
||||||
fontSize: 14,
|
fontSize: screenWidth * 0.036,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 1,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ class WelcomeScreen extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: screenHeight * 0.1),
|
SizedBox(height: screenHeight * 0.05),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user