Skip to content

Commit

Permalink
Merge pull request #38 from mahmoodhamdi:feature/add-shimmer-loading-…
Browse files Browse the repository at this point in the history
…effects

Add Shimmer Loading Effects for Better User Experience
  • Loading branch information
mahmoodhamdi authored Nov 17, 2024
2 parents 5f204d1 + b28e726 commit 47d71fa
Show file tree
Hide file tree
Showing 8 changed files with 330 additions and 52 deletions.
95 changes: 95 additions & 0 deletions lib/core/common/widgets/product_shimmer.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import 'package:flutter/material.dart';
import 'package:shimmer/shimmer.dart';
import 'package:t_store/core/utils/constants/colors.dart';
import 'package:t_store/core/utils/constants/sizes.dart';
import 'package:t_store/core/utils/helpers/helper_functions.dart';

class ProductShimmer extends StatelessWidget {
const ProductShimmer({super.key});

@override
Widget build(BuildContext context) {
final dark = THelperFunctions.isDarkMode(context);
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: TSizes.gridViewSpacing,
crossAxisSpacing: TSizes.gridViewSpacing,
mainAxisExtent: 288,
),
itemCount: 4, // Show 4 shimmer items while loading
itemBuilder: (_, __) => Container(
width: 180,
padding: const EdgeInsets.all(1),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(TSizes.productImageRadius),
color: dark ? TColors.darkerGrey : TColors.white,
),
child: Shimmer.fromColors(
baseColor: dark ? Colors.grey[850]! : Colors.grey[300]!,
highlightColor: dark ? Colors.grey[700]! : Colors.grey[100]!,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Image container shimmer
Container(
width: double.infinity,
height: 180,
decoration: BoxDecoration(
color: Colors.white,
borderRadius:
BorderRadius.circular(TSizes.productImageRadius),
),
),
const SizedBox(height: TSizes.spaceBtwItems),
// Title shimmer
Padding(
padding: const EdgeInsets.symmetric(horizontal: TSizes.sm),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: double.infinity,
height: 12,
color: Colors.white,
),
const SizedBox(height: TSizes.spaceBtwItems / 2),
// Brand shimmer
Container(
width: 100,
height: 10,
color: Colors.white,
),
const SizedBox(height: TSizes.spaceBtwItems),
// Price and cart button shimmer
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
width: 60,
height: 15,
color: Colors.white,
),
Container(
width: 40,
height: 40,
decoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
),
],
),
],
),
),
],
),
),
),
);
}
}
76 changes: 53 additions & 23 deletions lib/core/common/widgets/rounded_image.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:shimmer/shimmer.dart';
import 'package:t_store/core/common/view_models/rounded_image_view_model.dart';
import 'package:t_store/core/utils/constants/colors.dart';
import 'package:t_store/core/utils/helpers/helper_functions.dart';

class RoundedImage extends StatelessWidget {
const RoundedImage({
Expand All @@ -10,6 +13,8 @@ class RoundedImage extends StatelessWidget {
final RoundedImageModel roundedImageModel;
@override
Widget build(BuildContext context) {
final dark = THelperFunctions.isDarkMode(context);

return GestureDetector(
onTap: roundedImageModel.onTap,
child: Container(
Expand All @@ -21,30 +26,55 @@ class RoundedImage extends StatelessWidget {
color: roundedImageModel.backgroundColor,
),
child: ClipRRect(
borderRadius: roundedImageModel.applyImageRadius
? BorderRadius.circular(roundedImageModel.borderRadius)
: BorderRadius.zero,
child: roundedImageModel.isNetworkImage
? CachedNetworkImage(
imageUrl: roundedImageModel.image,
placeholderFadeInDuration: Duration.zero,
placeholder: (context, url) => SizedBox(
width: roundedImageModel.width,
height: roundedImageModel.height

),
errorWidget: (context, url, error) =>
const Icon(Icons.error),
color: roundedImageModel.overlayColor,
fit: roundedImageModel.fit,
)
: Image(
image: AssetImage(
roundedImageModel.image,
borderRadius: roundedImageModel.applyImageRadius
? BorderRadius.circular(roundedImageModel.borderRadius)
: BorderRadius.zero,
child: roundedImageModel.isNetworkImage
? CachedNetworkImage(
imageUrl: roundedImageModel.image,
placeholderFadeInDuration: Duration.zero,
placeholder: (context, url) => Shimmer.fromColors(
baseColor: dark ? Colors.grey[850]! : Colors.grey[300]!,
highlightColor:
dark ? Colors.grey[700]! : Colors.grey[100]!,
child: Container(
width: roundedImageModel.width,
height: roundedImageModel.height,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: roundedImageModel.applyImageRadius
? BorderRadius.circular(
roundedImageModel.borderRadius)
: null,
),
),
),
errorWidget: (context, url, error) => Container(
width: roundedImageModel.width,
height: roundedImageModel.height,
decoration: BoxDecoration(
color: dark ? TColors.darkerGrey : TColors.light,
borderRadius: roundedImageModel.applyImageRadius
? BorderRadius.circular(
roundedImageModel.borderRadius)
: null,
),
color: roundedImageModel.overlayColor,
fit: roundedImageModel.fit,
)),
child: Icon(
Icons.error,
color: dark ? TColors.light : TColors.dark,
),
),
color: roundedImageModel.overlayColor,
fit: roundedImageModel.fit,
)
: Image(
image: AssetImage(
roundedImageModel.image,
),
color: roundedImageModel.overlayColor,
fit: roundedImageModel.fit,
),
),
),
);
}
Expand Down
4 changes: 0 additions & 4 deletions lib/core/utils/exceptions/firebase_auth_exceptions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,10 @@ class TFirebaseAuthException implements Exception {
return 'The provided Cordova configuration is invalid.';
case 'app-deleted':
return 'This instance of FirebaseApp has been deleted.';
case 'user-disabled':
return 'The user account has been disabled.';
case 'user-token-mismatch':
return 'The provided user\'s token has a mismatch with the authenticated user\'s user ID.';
case 'web-storage-unsupported':
return 'Web storage is not supported or is disabled.';
case 'invalid-credential':
return 'The supplied credential is invalid. Please check the credential and try again.';
case 'app-not-authorized':
return 'The app is not authorized to use Firebase Authentication with the provided API key.';
case 'keychain-error':
Expand Down
23 changes: 13 additions & 10 deletions lib/core/utils/theme/widget_themes/checkbox_theme.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';

import '../../constants/colors.dart';
import '../../constants/sizes.dart';

Expand All @@ -8,16 +9,17 @@ class TCheckboxTheme {

/// Customizable Light Text Theme
static CheckboxThemeData lightCheckboxTheme = CheckboxThemeData(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(TSizes.xs)),
checkColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.selected)) {
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(TSizes.xs)),
checkColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return TColors.white;
} else {
return TColors.black;
}
}),
fillColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.selected)) {
fillColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return TColors.primary;
} else {
return Colors.transparent;
Expand All @@ -27,16 +29,17 @@ class TCheckboxTheme {

/// Customizable Dark Text Theme
static CheckboxThemeData darkCheckboxTheme = CheckboxThemeData(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(TSizes.xs)),
checkColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.selected)) {
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(TSizes.xs)),
checkColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return TColors.white;
} else {
return TColors.black;
}
}),
fillColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.selected)) {
fillColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return TColors.primary;
} else {
return Colors.transparent;
Expand Down
Loading

0 comments on commit 47d71fa

Please sign in to comment.