Browse Source

add: intro page

pull/1/head
AmirrezaChegini 1 month ago
parent
commit
2674dd196e
  1. BIN
      assets/fonts/Lexend-Black.ttf
  2. BIN
      assets/fonts/Lexend-Bold.ttf
  3. BIN
      assets/fonts/Lexend-ExtraBold.ttf
  4. BIN
      assets/fonts/Lexend-ExtraLight.ttf
  5. BIN
      assets/fonts/Lexend-Light.ttf
  6. BIN
      assets/fonts/Lexend-Medium.ttf
  7. BIN
      assets/fonts/Lexend-Regular.ttf
  8. BIN
      assets/fonts/Lexend-SemiBold.ttf
  9. BIN
      assets/fonts/Lexend-Thin.ttf
  10. BIN
      assets/images/question.png
  11. BIN
      assets/images/shia_mind.png
  12. 13
      lib/common_ui/resources/my_assets.dart
  13. 1
      lib/common_ui/resources/my_colors.dart
  14. 53
      lib/common_ui/resources/my_text_style.dart
  15. 10
      lib/common_ui/theme/my_theme.dart
  16. 13
      lib/core/params/intro_params.dart
  17. 10
      lib/core/routers/my_routes.dart
  18. 45
      lib/core/widgets/image/my_image.dart
  19. 28
      lib/features/intro/data/datasource/intro_datasource.dart
  20. 13
      lib/features/intro/data/model/intro_model.dart
  21. 29
      lib/features/intro/data/repository_impl/intro_repository_impl.dart
  22. 14
      lib/features/intro/domain/entity/intro_entity.dart
  23. 8
      lib/features/intro/domain/repository/intro_repository.dart
  24. 19
      lib/features/intro/domain/usecases/get_intro_usecase.dart
  25. 20
      lib/features/intro/presentation/binding/intro_binding.dart
  26. 54
      lib/features/intro/presentation/controller/intro_controller.dart
  27. 99
      lib/features/intro/presentation/ui/intro_page.dart
  28. 49
      lib/features/intro/presentation/ui/widgets/intro_loading.dart
  29. 9
      lib/init_bindings.dart
  30. 3
      lib/l10n/app_en.arb
  31. 6
      lib/l10n/app_localizations.dart
  32. 3
      lib/l10n/app_localizations_en.dart
  33. 2
      lib/main.dart
  34. 22
      pubspec.yaml

BIN
assets/fonts/Lexend-Black.ttf

BIN
assets/fonts/Lexend-Bold.ttf

BIN
assets/fonts/Lexend-ExtraBold.ttf

BIN
assets/fonts/Lexend-ExtraLight.ttf

BIN
assets/fonts/Lexend-Light.ttf

BIN
assets/fonts/Lexend-Medium.ttf

BIN
assets/fonts/Lexend-Regular.ttf

BIN
assets/fonts/Lexend-SemiBold.ttf

BIN
assets/fonts/Lexend-Thin.ttf

BIN
assets/images/question.png

After

Width: 25  |  Height: 38  |  Size: 1.1 KiB

BIN
assets/images/shia_mind.png

After

Width: 169  |  Height: 130  |  Size: 32 KiB

13
lib/common_ui/resources/my_assets.dart

@ -4,19 +4,22 @@ class MyAssets {
factory MyAssets() => _i;
/// ----- Images -----
static const String sample = 'assets/image/sample.png';
static const String sample = 'assets/images/sample.png';
static const String shiaMind = 'assets/images/shia_mind.png';
static const String question = 'assets/images/question.png';
/// ----- Svg -----
static const String sampleSvg = 'assets/image/sample.svg';
static const String sampleSvg = 'assets/svg/sample.svg';
/// ----- Audios -----
static const String sampleAudio = 'assets/audio/sample.mp3';
static const String sampleAudio = 'assets/audios/sample.mp3';
/// ----- Videos -----
static const String sampleVideo = 'assets/video/sample.mp4';
static const String sampleVideo = 'assets/videos/sample.mp4';
static const List<String> images = [
sample,
shiaMind,
question,
];
}

1
lib/common_ui/resources/my_colors.dart

@ -8,4 +8,5 @@ class MyColors {
static const Color white = Colors.white;
static const Color black = Colors.black;
static const Color transparent = Colors.transparent;
static const Color introBackgroundColor = Color(0xFF160C30);
}

53
lib/common_ui/resources/my_text_style.dart

@ -1,15 +1,54 @@
import 'package:flutter/material.dart';
class MyTextStyle {
static const MyTextStyle _i = MyTextStyle._internal();
const MyTextStyle._internal();
factory MyTextStyle() => _i;
class Lexend {
static const Lexend _i = Lexend._internal();
const Lexend._internal();
factory Lexend() => _i;
static const String fontFamily = '';
static const String fontFamily = 'Lexend';
static const TextStyle lightXS = TextStyle(
static const TextStyle thin = TextStyle(
fontFamily: fontFamily,
fontWeight: FontWeight.w100,
);
static const TextStyle extraLight = TextStyle(
fontFamily: fontFamily,
fontWeight: FontWeight.w200,
);
static const TextStyle light = TextStyle(
fontFamily: fontFamily,
fontWeight: FontWeight.w300,
);
static const TextStyle regular = TextStyle(
fontFamily: fontFamily,
fontSize: 10,
fontWeight: FontWeight.w400,
);
static const TextStyle medium = TextStyle(
fontFamily: fontFamily,
fontWeight: FontWeight.w500,
);
static const TextStyle semiBold = TextStyle(
fontFamily: fontFamily,
fontWeight: FontWeight.w600,
);
static const TextStyle bold = TextStyle(
fontFamily: fontFamily,
fontWeight: FontWeight.w700,
);
static const TextStyle extraBold = TextStyle(
fontFamily: fontFamily,
fontWeight: FontWeight.w800,
);
static const TextStyle black = TextStyle(
fontFamily: fontFamily,
fontWeight: FontWeight.w900,
);
}

10
lib/common_ui/theme/my_theme.dart

@ -4,6 +4,8 @@ import 'package:get/get.dart';
enum ColorsName {
primaryColor,
noColor,
introBackgroundColor,
}
class MyTheme {
@ -16,11 +18,15 @@ class MyTheme {
static final ThemeData dark = ThemeData(brightness: Brightness.light);
static Map<ColorsName, Color> get lightColors => {
ColorsName.primaryColor: MyColors.black,
ColorsName.primaryColor: MyColors.white,
ColorsName.noColor: MyColors.transparent,
ColorsName.introBackgroundColor: MyColors.introBackgroundColor,
};
static Map<ColorsName, Color> get darkColors => {
ColorsName.primaryColor: MyColors.white,
ColorsName.noColor: MyColors.transparent,
ColorsName.introBackgroundColor: MyColors.introBackgroundColor,
};
}
@ -29,4 +35,6 @@ extension ThemeExtension on BuildContext {
Get.isDarkMode ? MyTheme.darkColors : MyTheme.lightColors;
Color get primaryColor => customColors[ColorsName.primaryColor]!;
Color get noColor => customColors[ColorsName.noColor]!;
Color get introBackgroundColor => customColors[ColorsName.introBackgroundColor]!;
}

13
lib/core/params/intro_params.dart

@ -0,0 +1,13 @@
class IntroParams {
int? id;
IntroParams({this.id});
IntroParams copyWith({
int? id,
}) {
return IntroParams(
id: id ?? this.id,
);
}
}

10
lib/core/routers/my_routes.dart

@ -1,3 +1,5 @@
import 'package:shia_game_flutter/features/intro/presentation/binding/intro_binding.dart';
import 'package:shia_game_flutter/features/intro/presentation/ui/intro_page.dart';
import 'package:shia_game_flutter/features/sample/presentation/binding/sample_binding.dart';
import 'package:shia_game_flutter/features/sample/presentation/ui/sample_page.dart';
import 'package:get/get.dart';
@ -8,6 +10,7 @@ class Routes {
factory Routes() => _i;
static const String samplePage = '/sample_page';
static const String introPage = '/intro_page';
}
List<GetPage> get appPages => [
@ -16,4 +19,9 @@ List<GetPage> get appPages => [
page: () => const SamplePage(),
binding: SampleBinding(),
),
];
GetPage(
name: Routes.introPage,
page: () => const IntroPage(),
binding: IntroBinding(),
),
];

45
lib/core/widgets/image/my_image.dart

@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
import 'package:vector_graphics/vector_graphics.dart';
class MyImage extends StatelessWidget {
const MyImage({
super.key,
required this.asset,
this.size,
this.fit = BoxFit.contain,
this.color,
});
final String asset;
final double? size;
final BoxFit fit;
final Color? color;
@override
Widget build(BuildContext context) {
return Builder(
builder: (context) {
if (asset.endsWith('.jpg') ||
asset.endsWith('.jpeg') ||
asset.endsWith('.png')) {
return Image.asset(
asset,
width: size,
height: size,
fit: fit,
color: color,
);
} else {
return VectorGraphic(
loader: AssetBytesLoader(asset),
height: size,
fit: fit,
colorFilter: color == null
? null
: ColorFilter.mode(color!, BlendMode.srcIn),
);
}
},
);
}
}

28
lib/features/intro/data/datasource/intro_datasource.dart

@ -0,0 +1,28 @@
import 'package:shia_game_flutter/core/constants/my_api.dart';
import 'package:shia_game_flutter/core/network/http_request.dart';
import 'package:shia_game_flutter/core/params/intro_params.dart';
import 'package:shia_game_flutter/core/response/base_response.dart';
import 'package:shia_game_flutter/features/intro/data/model/intro_model.dart';
import 'package:shia_game_flutter/features/intro/domain/entity/intro_entity.dart';
abstract class IIntroDatasource {
Future<IntroEntity> getData({required IntroParams params});
}
class IntroDatasourceImpl implements IIntroDatasource {
final IHttpRequest httpRequest;
const IntroDatasourceImpl(this.httpRequest);
@override
Future<IntroEntity> getData({required IntroParams params}) async {
final response = await httpRequest.get(
path: MyApi.baseUrl,
);
return BaseResponse.getData<IntroEntity>(
response?['data'],
(json) => IntroModel.fromJson(json),
);
}
}

13
lib/features/intro/data/model/intro_model.dart

@ -0,0 +1,13 @@
import 'package:shia_game_flutter/features/intro/domain/entity/intro_entity.dart';
class IntroModel extends IntroEntity {
const IntroModel({
super.id,
});
factory IntroModel.fromJson(Map<String, dynamic> json) {
return IntroModel(
id: json['id'],
);
}
}

29
lib/features/intro/data/repository_impl/intro_repository_impl.dart

@ -0,0 +1,29 @@
import 'package:flutter/foundation.dart';
import 'package:shia_game_flutter/core/error_handler/my_exception.dart';
import 'package:shia_game_flutter/core/params/intro_params.dart';
import 'package:shia_game_flutter/core/utils/data_state.dart';
import 'package:shia_game_flutter/features/intro/data/datasource/intro_datasource.dart';
import 'package:shia_game_flutter/features/intro/domain/entity/intro_entity.dart';
import 'package:shia_game_flutter/features/intro/domain/repository/intro_repository.dart';
class IntroRepositoryImpl implements IIntroRepository {
final IIntroDatasource datasource;
const IntroRepositoryImpl(this.datasource);
@override
Future<DataState<IntroEntity, MyException>> getData({required IntroParams params}) async {
try {
final IntroEntity response = await datasource.getData(params: params);
return DataState.success(response);
} on MyException catch (e) {
return DataState.error(e);
} catch (e) {
if (kDebugMode) {
rethrow;
} else {
return DataState.error(MyException(errorMessage: '$e'));
}
}
}
}

14
lib/features/intro/domain/entity/intro_entity.dart

@ -0,0 +1,14 @@
import 'package:equatable/equatable.dart';
class IntroEntity extends Equatable {
final int? id;
const IntroEntity({
this.id,
});
@override
List<Object?> get props => [
id,
];
}

8
lib/features/intro/domain/repository/intro_repository.dart

@ -0,0 +1,8 @@
import 'package:shia_game_flutter/core/error_handler/my_exception.dart';
import 'package:shia_game_flutter/core/params/intro_params.dart';
import 'package:shia_game_flutter/core/utils/data_state.dart';
import 'package:shia_game_flutter/features/intro/domain/entity/intro_entity.dart';
abstract class IIntroRepository {
Future<DataState<IntroEntity, MyException>> getData({required IntroParams params});
}

19
lib/features/intro/domain/usecases/get_intro_usecase.dart

@ -0,0 +1,19 @@
import 'package:shia_game_flutter/core/error_handler/my_exception.dart';
import 'package:shia_game_flutter/core/params/intro_params.dart';
import 'package:shia_game_flutter/core/usecase/usecase.dart';
import 'package:shia_game_flutter/core/utils/data_state.dart';
import 'package:shia_game_flutter/features/intro/domain/entity/intro_entity.dart';
import 'package:shia_game_flutter/features/intro/domain/repository/intro_repository.dart';
class GetIntroUseCase implements UseCase<IntroEntity, IntroParams> {
final IIntroRepository repository;
const GetIntroUseCase(this.repository);
@override
Future<DataState<IntroEntity, MyException>> call(IntroParams params) {
return repository.getData(params: params);
}
}

20
lib/features/intro/presentation/binding/intro_binding.dart

@ -0,0 +1,20 @@
import 'package:shia_game_flutter/features/intro/presentation/controller/intro_controller.dart';
import 'package:get/get.dart';
class IntroBinding extends Bindings {
@override
void dependencies() {
Get.put<IntroController>(IntroController(Get.find()));
}
Future<void> deleteBindings() async {
await Future.wait([
Get.delete<IntroController>(),
]);
}
Future<void> refreshBinding() async {
await deleteBindings();
dependencies();
}
}

54
lib/features/intro/presentation/controller/intro_controller.dart

@ -0,0 +1,54 @@
import 'package:flutter/cupertino.dart';
import 'package:shia_game_flutter/core/params/intro_params.dart';
import 'package:shia_game_flutter/core/status/base_status.dart';
import 'package:shia_game_flutter/features/intro/domain/entity/intro_entity.dart';
import 'package:shia_game_flutter/features/intro/domain/usecases/get_intro_usecase.dart';
import 'package:get/get.dart';
class IntroController extends GetxController with StateMixin {
/// ----- Constructor -----
IntroController(this.getIntroUseCase);
@override
void onInit() {
super.onInit();
change('', status: RxStatus.success());
}
@override
void onClose() {
textEditingController.dispose();
super.onClose();
}
/// ----- UseCases -----
final GetIntroUseCase getIntroUseCase;
/// ----- Variables -----
final Rx<IntroParams> introParams = Rx(IntroParams());
final Rx<IntroEntity> introEntity = Rx(const IntroEntity());
/// ------ Controllers ------
final TextEditingController textEditingController = TextEditingController();
/// ------ Statuses ------
final Rx<BaseStatus> getIntroStatus = Rx(const BaseInit());
/// ------ Functions ------
/// ------ Api Calls ------
Future<void> getIntro() async {
change('', status: RxStatus.loading());
await getIntroUseCase(introParams.value).then(
(value) => value.fold(
(data) {
introEntity.value = data;
change('', status: RxStatus.success());
},
(error) {
change('', status: RxStatus.error(error.errorMessage));
},
),
);
}
}

99
lib/features/intro/presentation/ui/intro_page.dart

@ -0,0 +1,99 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:shia_game_flutter/common_ui/resources/my_assets.dart';
import 'package:shia_game_flutter/common_ui/resources/my_spaces.dart';
import 'package:shia_game_flutter/common_ui/resources/my_text_style.dart';
import 'package:shia_game_flutter/common_ui/theme/my_theme.dart';
import 'package:shia_game_flutter/core/utils/my_localization.dart';
import 'package:shia_game_flutter/core/utils/screen_size.dart';
import 'package:shia_game_flutter/core/widgets/image/my_image.dart';
import 'package:shia_game_flutter/features/intro/presentation/controller/intro_controller.dart';
import 'package:shia_game_flutter/features/intro/presentation/ui/widgets/intro_loading.dart';
class IntroPage extends GetView<IntroController> {
const IntroPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: context.introBackgroundColor,
body: SafeArea(
child: SizedBox.expand(
child: Stack(
alignment: Alignment.center,
children: [
_logo(context),
_bottomLoading(context),
],
),
),
),
);
}
Stack _logo(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: [
PositionedDirectional(
top: context.heightScreen / 3.2,
end: 50,
child: MyImage(asset: MyAssets.question),
),
PositionedDirectional(
top: context.heightScreen / 3.5,
end: context.widthScreen / 2.4,
child: MyImage(asset: MyAssets.question, size: 30),
),
PositionedDirectional(
top: context.heightScreen / 2.8,
start: 80,
child: MyImage(asset: MyAssets.question, size: 24),
),
PositionedDirectional(
bottom: context.heightScreen / 3.2,
start: 80,
child: MyImage(asset: MyAssets.question),
),
PositionedDirectional(
bottom: context.heightScreen / 3.2,
end: 50,
child: MyImage(asset: MyAssets.question, size: 20),
),
PositionedDirectional(
bottom: context.heightScreen / 2.6,
start: context.widthScreen / 2,
child: MyImage(asset: MyAssets.question, size: 20),
),
Container(
width: context.widthScreen,
height: context.heightScreen,
decoration: ShapeDecoration(
gradient: RadialGradient(
colors: [const Color(0xFF321A6D), const Color(0x00160C30)],
),
shape: OvalBorder(),
),
),
MyImage(asset: MyAssets.shiaMind),
],
);
}
Positioned _bottomLoading(BuildContext context) {
return Positioned(
bottom: MySpaces.s16,
child: Column(
spacing: MySpaces.s10,
mainAxisSize: MainAxisSize.min,
children: [
Text(
context.translate.loading,
style: Lexend.regular.copyWith(fontSize: MySpaces.s14),
),
IntroLoading(),
],
),
);
}
}

49
lib/features/intro/presentation/ui/widgets/intro_loading.dart

@ -0,0 +1,49 @@
import 'package:flutter/material.dart';
import 'package:shia_game_flutter/common_ui/resources/my_spaces.dart';
class IntroLoading extends StatelessWidget {
const IntroLoading({
super.key,
});
@override
Widget build(BuildContext context) {
return Container(
width: 188,
height: MySpaces.s16,
padding: EdgeInsetsDirectional.only(
start: MySpaces.s2,
top: MySpaces.s2,
bottom: MySpaces.s2,
end: MySpaces.s20,
),
decoration: ShapeDecoration(
shape: StadiumBorder(
side: BorderSide(
width: 1,
color: Color(0XFFA579EA),
),
),
),
child: Container(
height: MySpaces.s14,
decoration: ShapeDecoration(
shape: StadiumBorder(
side: BorderSide(
width: 1,
color: Colors.white.withValues(alpha: 0.5),
),
),
gradient: LinearGradient(
begin: AlignmentDirectional.centerStart,
end: AlignmentDirectional.centerEnd,
colors: [
Color(0XFF9C7EEC),
Color(0XFF3BBDFF),
],
),
),
),
);
}
}

9
lib/init_bindings.dart

@ -1,6 +1,10 @@
import 'package:get/get.dart';
import 'package:shia_game_flutter/core/network/http_request.dart';
import 'package:shia_game_flutter/core/network/http_request_impl.dart';
import 'package:shia_game_flutter/features/intro/data/datasource/intro_datasource.dart';
import 'package:shia_game_flutter/features/intro/data/repository_impl/intro_repository_impl.dart';
import 'package:shia_game_flutter/features/intro/domain/repository/intro_repository.dart';
import 'package:shia_game_flutter/features/intro/domain/usecases/get_intro_usecase.dart';
import 'package:shia_game_flutter/features/sample/data/datasource/sample_datasource.dart';
import 'package:shia_game_flutter/features/sample/data/repository_impl/sample_repository_impl.dart';
import 'package:shia_game_flutter/features/sample/domain/repository/sample_repository.dart';
@ -14,4 +18,9 @@ void initBindings() {
Get.lazyPut<ISampleDatasource>(() => SampleDatasourceImpl(Get.find()));
Get.lazyPut<ISampleRepository>(() => SampleRepositoryImpl(Get.find()));
Get.lazyPut<GetSampleUseCase>(() => GetSampleUseCase(Get.find()));
/// ----- Intro Feature -----
Get.lazyPut<IIntroDatasource>(() => IntroDatasourceImpl(Get.find()));
Get.lazyPut<IIntroRepository>(() => IntroRepositoryImpl(Get.find()));
Get.lazyPut<GetIntroUseCase>(() => GetIntroUseCase(Get.find()));
}

3
lib/l10n/app_en.arb

@ -1,3 +1,4 @@
{
"@@locale": "en"
"@@locale": "en",
"loading": "Loading..."
}

6
lib/l10n/app_localizations.dart

@ -93,6 +93,12 @@ abstract class AppLocalizations {
/// A list of this localizations delegate's supported locales.
static const List<Locale> supportedLocales = <Locale>[Locale('en')];
/// No description provided for @loading.
///
/// In en, this message translates to:
/// **'Loading...'**
String get loading;
}
class _AppLocalizationsDelegate

3
lib/l10n/app_localizations_en.dart

@ -7,4 +7,7 @@ import 'app_localizations.dart';
/// The translations for English (`en`).
class AppLocalizationsEn extends AppLocalizations {
AppLocalizationsEn([String locale = 'en']) : super(locale);
@override
String get loading => 'Loading...';
}

2
lib/main.dart

@ -31,7 +31,7 @@ class MainApp extends StatelessWidget {
fallbackLocale: const Locale('en', 'US'),
supportedLocales: const [Locale('en', 'US')],
getPages: appPages,
initialRoute: Routes.samplePage,
initialRoute: Routes.introPage,
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,

22
pubspec.yaml

@ -38,3 +38,25 @@ flutter:
- path: assets/svg/
transformers:
- package: vector_graphics_compiler
fonts:
- family: Lexend
fonts:
- asset: assets/fonts/Lexend-Thin.ttf
weight: 100
- asset: assets/fonts/Lexend-ExtraLight.ttf
weight: 200
- asset: assets/fonts/Lexend-Light.ttf
weight: 300
- asset: assets/fonts/Lexend-Regular.ttf
weight: 400
- asset: assets/fonts/Lexend-Medium.ttf
weight: 500
- asset: assets/fonts/Lexend-SemiBold.ttf
weight: 600
- asset: assets/fonts/Lexend-Bold.ttf
weight: 700
- asset: assets/fonts/Lexend-ExtraBold.ttf
weight: 800
- asset: assets/fonts/Lexend-Black.ttf
weight: 900
Loading…
Cancel
Save