From de5895f00da2a7084136147c75bd980929744908 Mon Sep 17 00:00:00 2001 From: Veli Bacik Date: Wed, 24 Feb 2021 03:57:12 +0300 Subject: [PATCH] game page design and service completed --- asset/lang/en-US.json | 13 +++ ios/Podfile.lock | 15 +++ ios/Runner.xcodeproj/project.pbxproj | 8 +- lib/core/init/lang/locale_keys.g.dart | 11 ++ lib/core/init/theme/app_theme_light.dart | 5 +- lib/main.dart | 4 +- lib/product/widget/button/header_button.dart | 44 +++++++ lib/product/widget/card/game_card.dart | 29 +++++ lib/product/widget/grid/game_grid_view.dart | 29 +++++ lib/product/widget/pageview/game_slider.dart | 55 +++++++++ .../_product/enum/network_route_enum.dart | 8 +- lib/view/home/game/model/game_enums.dart | 1 + lib/view/home/game/model/game_model.dart | 31 +++++ lib/view/home/game/model/game_model.g.dart | 25 ++++ lib/view/home/game/model/game_view_state.dart | 7 ++ lib/view/home/game/model/slider_model.dart | 23 ++++ lib/view/home/game/model/slider_model.g.dart | 22 ++++ lib/view/home/game/service/IGameService.dart | 16 +++ lib/view/home/game/service/game_service.dart | 31 +++++ lib/view/home/game/view/game_view.dart | 108 ++++++++++++++++++ .../game/view/subview/game_view_cards.dart | 44 +++++++ .../home/game/viewmodel/game_view_model.dart | 70 ++++++++++++ .../game/viewmodel/game_view_model.g.dart | 55 +++++++++ pubspec.lock | 95 ++++++++++++++- pubspec.yaml | 2 + 25 files changed, 740 insertions(+), 11 deletions(-) create mode 100644 lib/product/widget/button/header_button.dart create mode 100644 lib/product/widget/card/game_card.dart create mode 100644 lib/product/widget/grid/game_grid_view.dart create mode 100644 lib/product/widget/pageview/game_slider.dart create mode 100644 lib/view/home/game/model/game_enums.dart create mode 100644 lib/view/home/game/model/game_model.dart create mode 100644 lib/view/home/game/model/game_model.g.dart create mode 100644 lib/view/home/game/model/game_view_state.dart create mode 100644 lib/view/home/game/model/slider_model.dart create mode 100644 lib/view/home/game/model/slider_model.g.dart create mode 100644 lib/view/home/game/service/IGameService.dart create mode 100644 lib/view/home/game/service/game_service.dart create mode 100644 lib/view/home/game/view/game_view.dart create mode 100644 lib/view/home/game/view/subview/game_view_cards.dart create mode 100644 lib/view/home/game/viewmodel/game_view_model.dart create mode 100644 lib/view/home/game/viewmodel/game_view_model.g.dart diff --git a/asset/lang/en-US.json b/asset/lang/en-US.json index 1cb2a46..1933138 100644 --- a/asset/lang/en-US.json +++ b/asset/lang/en-US.json @@ -35,6 +35,19 @@ "findFriends":"Find Friends", "follow":"Follow", "following":"Following" + }, + "game":{ + "title":"Unity Game", + "newUpdate":"New Updated Games", + "viewAll":"View All", + "topDownload":"Top Downloads", + "tabbar":{ + "tab1":"RACING", + "tab2":"SIMULATION", + "tab3":"CASUAL", + "tab4":"CARTOONY", + "tab5":"TOYS" + } } } } \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 380d632..a43fbaf 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2,6 +2,9 @@ PODS: - device_info (0.0.1): - Flutter - Flutter (1.0.0) + - FMDB (2.7.5): + - FMDB/standard (= 2.7.5) + - FMDB/standard (2.7.5) - path_provider (0.0.1): - Flutter - path_provider_linux (0.0.1): @@ -22,6 +25,9 @@ PODS: - Flutter - shared_preferences_windows (0.0.1): - Flutter + - sqflite (0.0.2): + - Flutter + - FMDB (>= 2.7.5) - url_launcher (0.0.1): - Flutter - url_launcher_linux (0.0.1): @@ -46,12 +52,17 @@ DEPENDENCIES: - shared_preferences_macos (from `.symlinks/plugins/shared_preferences_macos/ios`) - shared_preferences_web (from `.symlinks/plugins/shared_preferences_web/ios`) - shared_preferences_windows (from `.symlinks/plugins/shared_preferences_windows/ios`) + - sqflite (from `.symlinks/plugins/sqflite/ios`) - url_launcher (from `.symlinks/plugins/url_launcher/ios`) - url_launcher_linux (from `.symlinks/plugins/url_launcher_linux/ios`) - url_launcher_macos (from `.symlinks/plugins/url_launcher_macos/ios`) - url_launcher_web (from `.symlinks/plugins/url_launcher_web/ios`) - url_launcher_windows (from `.symlinks/plugins/url_launcher_windows/ios`) +SPEC REPOS: + trunk: + - FMDB + EXTERNAL SOURCES: device_info: :path: ".symlinks/plugins/device_info/ios" @@ -77,6 +88,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/shared_preferences_web/ios" shared_preferences_windows: :path: ".symlinks/plugins/shared_preferences_windows/ios" + sqflite: + :path: ".symlinks/plugins/sqflite/ios" url_launcher: :path: ".symlinks/plugins/url_launcher/ios" url_launcher_linux: @@ -91,6 +104,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: device_info: d7d233b645a32c40dfdc212de5cf646ca482f175 Flutter: 0e3d915762c693b495b44d77113d4970485de6ec + FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c path_provider_linux: 4d630dc393e1f20364f3e3b4a2ff41d9674a84e4 path_provider_macos: f760a3c5b04357c380e2fddb6f9db6f3015897e0 @@ -101,6 +115,7 @@ SPEC CHECKSUMS: shared_preferences_macos: f3f29b71ccbb56bf40c9dd6396c9acf15e214087 shared_preferences_web: 141cce0c3ed1a1c5bf2a0e44f52d31eeb66e5ea9 shared_preferences_windows: 36b76d6f54e76ead957e60b49e2f124b4cd3e6ae + sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef url_launcher_linux: ac237cb7a8058736e4aae38bdbcc748a4b394cc0 url_launcher_macos: fd7894421cd39320dce5f292fc99ea9270b2a313 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 3af38e9..62fb9ef 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 51; objects = { /* Begin PBXBuildFile section */ @@ -339,7 +339,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -429,7 +429,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -478,7 +478,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/lib/core/init/lang/locale_keys.g.dart b/lib/core/init/lang/locale_keys.g.dart index bb04224..6c40724 100644 --- a/lib/core/init/lang/locale_keys.g.dart +++ b/lib/core/init/lang/locale_keys.g.dart @@ -34,6 +34,17 @@ abstract class LocaleKeys { static const home_social_follow = 'home.social.follow'; static const home_social_following = 'home.social.following'; static const home_social = 'home.social'; + static const home_game_title = 'home.game.title'; + static const home_game_newUpdate = 'home.game.newUpdate'; + static const home_game_viewAll = 'home.game.viewAll'; + static const home_game_topDownload = 'home.game.topDownload'; + static const home_game_tabbar_tab1 = 'home.game.tabbar.tab1'; + static const home_game_tabbar_tab2 = 'home.game.tabbar.tab2'; + static const home_game_tabbar_tab3 = 'home.game.tabbar.tab3'; + static const home_game_tabbar_tab4 = 'home.game.tabbar.tab4'; + static const home_game_tabbar_tab5 = 'home.game.tabbar.tab5'; + static const home_game_tabbar = 'home.game.tabbar'; + static const home_game = 'home.game'; static const home = 'home'; } diff --git a/lib/core/init/theme/app_theme_light.dart b/lib/core/init/theme/app_theme_light.dart index 7a2e13b..dc75ae7 100644 --- a/lib/core/init/theme/app_theme_light.dart +++ b/lib/core/init/theme/app_theme_light.dart @@ -18,7 +18,8 @@ class AppThemeLight extends AppTheme with ILightTheme { fontFamily: ApplicationConstants.FONT_FAMILY, colorScheme: _appColorScheme, textTheme: textTheme(), - appBarTheme: ThemeData.light().appBarTheme.copyWith(brightness: Brightness.light, iconTheme: IconThemeData(color: Colors.black87, size: 21)), + appBarTheme: ThemeData.light().appBarTheme.copyWith( + brightness: Brightness.light, color: Colors.transparent, elevation: 0, iconTheme: IconThemeData(color: Colors.black87, size: 21)), inputDecorationTheme: InputDecorationTheme( focusColor: Colors.black12, labelStyle: TextStyle(), @@ -67,7 +68,7 @@ class AppThemeLight extends AppTheme with ILightTheme { onSecondary: Colors.black, //x onSurface: Colors.white30, onBackground: Colors.black12, - onError: Color(0xffffc93c), //xx + onError: Color(0xFFF9B916), //xx brightness: Brightness.light); } } diff --git a/lib/main.dart b/lib/main.dart index 2e927e3..8905b53 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,7 +9,7 @@ import 'core/init/navigation/navigation_route.dart'; import 'core/init/navigation/navigation_service.dart'; import 'core/init/notifier/provider_list.dart'; import 'core/init/notifier/theme_notifer.dart'; -import 'view/home/social/view/social_view.dart'; +import 'view/home/game/view/game_view.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); @@ -26,7 +26,7 @@ class MyApp extends StatelessWidget { return MaterialApp( debugShowCheckedModeBanner: false, theme: Provider.of(context, listen: false).currentTheme, - home: SocialView(), + home: GameView(), onGenerateRoute: NavigationRoute.instance.generateRoute, navigatorKey: NavigationService.instance.navigatorKey, ); diff --git a/lib/product/widget/button/header_button.dart b/lib/product/widget/button/header_button.dart new file mode 100644 index 0000000..bb3c31b --- /dev/null +++ b/lib/product/widget/button/header_button.dart @@ -0,0 +1,44 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:kartal/kartal.dart'; + +import '../../../core/init/lang/locale_keys.g.dart'; + +class HeaderButton extends StatelessWidget { + final String titleText; + final VoidCallback onPressed; + + const HeaderButton({Key key, this.titleText, this.onPressed}) : super(key: key); + @override + Widget build(BuildContext context) { + return Padding( + padding: context.horizontalPaddingLow, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [buildTextTitle(context), buildFlatButtonRight(context)], + ), + ); + } + + Text buildTextTitle(BuildContext context) { + return Text( + titleText.tr(), + style: context.textTheme.headline6.copyWith(color: context.colorScheme.onError), + ); + } + + FlatButton buildFlatButtonRight(BuildContext context) { + return FlatButton( + padding: EdgeInsets.zero, + onPressed: () { + onPressed(); + }, + child: Wrap( + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + Text(LocaleKeys.home_game_viewAll.tr(), style: context.textTheme.subtitle2.copyWith(color: context.colorScheme.onError)), + Icon(Icons.arrow_right, color: context.colorScheme.onError) + ], + )); + } +} diff --git a/lib/product/widget/card/game_card.dart b/lib/product/widget/card/game_card.dart new file mode 100644 index 0000000..d426d86 --- /dev/null +++ b/lib/product/widget/card/game_card.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +import '../../../core/extension/context_extension.dart'; +import '../../../view/home/game/model/game_model.dart'; + +class GameCard extends StatelessWidget { + final GameModel model; + final VoidCallback onPressed; + + const GameCard({Key key, this.model, this.onPressed}) : super(key: key); + @override + Widget build(BuildContext context) { + return Card( + child: Padding( + padding: context.paddingLow, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Center(child: Image.network(model.image)), + ), + Text(model.name), + Text('\$ ${model.money}') + ], + ), + ), + ); + } +} diff --git a/lib/product/widget/grid/game_grid_view.dart b/lib/product/widget/grid/game_grid_view.dart new file mode 100644 index 0000000..2cff3fc --- /dev/null +++ b/lib/product/widget/grid/game_grid_view.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +import '../../../view/home/game/model/game_model.dart'; +import '../card/game_card.dart'; + +class GameGrid extends StatelessWidget { + final List models; + final void Function(GameModel item, int indx) onPressed; + + const GameGrid({Key key, this.models, this.onPressed}) : super(key: key); + @override + Widget build(BuildContext context) { + return GridView.builder( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + childAspectRatio: 0.8, + ), + itemCount: 3, + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemBuilder: (context, index) => GameCard( + model: models[index], + onPressed: () { + onPressed(models[index], index); + }, + ), + ); + } +} diff --git a/lib/product/widget/pageview/game_slider.dart b/lib/product/widget/pageview/game_slider.dart new file mode 100644 index 0000000..2541555 --- /dev/null +++ b/lib/product/widget/pageview/game_slider.dart @@ -0,0 +1,55 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:kartal/kartal.dart'; + +import '../../../view/home/game/model/slider_model.dart'; + +class GameSlider extends StatefulWidget { + final List sliderModel; + + const GameSlider({Key key, this.sliderModel}) : super(key: key); + + @override + _GameSliderState createState() => _GameSliderState(); +} + +class _GameSliderState extends State { + int _selectedValueIndex = 0; + @override + Widget build(BuildContext context) { + return Column( + children: [Expanded(flex: 10, child: buildPageView()), Expanded(child: buildListViewCirleIndicator())], + ); + } + + PageView buildPageView() { + return PageView.builder( + controller: PageController(viewportFraction: 0.8), + onPageChanged: _changeValue, + itemCount: widget.sliderModel.length, + itemBuilder: (context, index) => buildCardImage(index), + ); + } + + void _changeValue(int index) { + _selectedValueIndex = index; + setState(() {}); + } + + Widget buildCardImage(int index) => CachedNetworkImage(imageUrl: widget.sliderModel[index].image); + + ListView buildListViewCirleIndicator() { + return ListView.builder( + itemCount: widget.sliderModel.length, + shrinkWrap: true, + scrollDirection: Axis.horizontal, + itemBuilder: (context, index) => Padding( + padding: EdgeInsets.all(context.dynamicWidth(0.01)), + child: CircleAvatar( + backgroundColor: _selectedValueIndex == index ? context.colorScheme.onError : context.colorScheme.onError.withOpacity(0.1), + radius: 10, + ), + ), + ); + } +} diff --git a/lib/view/_product/enum/network_route_enum.dart b/lib/view/_product/enum/network_route_enum.dart index afd6d84..c81780f 100644 --- a/lib/view/_product/enum/network_route_enum.dart +++ b/lib/view/_product/enum/network_route_enum.dart @@ -1,4 +1,4 @@ -enum NetworkRoutes { LOGIN, BUILD_HOME, FRIENDS } +enum NetworkRoutes { LOGIN, BUILD_HOME, FRIENDS, GAME, SLIDER } extension NetwrokRoutesString on NetworkRoutes { String get rawValue { @@ -9,6 +9,12 @@ extension NetwrokRoutesString on NetworkRoutes { return 'house'; case NetworkRoutes.FRIENDS: return 'friends'; + + case NetworkRoutes.GAME: + return 'games'; + + case NetworkRoutes.SLIDER: + return 'slider'; default: throw Exception('Routes Not FouND'); } diff --git a/lib/view/home/game/model/game_enums.dart b/lib/view/home/game/model/game_enums.dart new file mode 100644 index 0000000..852b93a --- /dev/null +++ b/lib/view/home/game/model/game_enums.dart @@ -0,0 +1 @@ +enum GameEnum { NEW, TOP } diff --git a/lib/view/home/game/model/game_model.dart b/lib/view/home/game/model/game_model.dart new file mode 100644 index 0000000..fc5c165 --- /dev/null +++ b/lib/view/home/game/model/game_model.dart @@ -0,0 +1,31 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:vexana/vexana.dart'; + +part 'game_model.g.dart'; + +@JsonSerializable() +class GameModel extends INetworkModel { + @JsonKey(name: '_id') + String sId; + String image; + String name; + int money; + int category; + GameModel({ + this.sId, + this.image, + this.name, + this.money, + this.category, + }); + + @override + GameModel fromJson(Map json) { + return _$GameModelFromJson(json); + } + + @override + Map toJson() { + return _$GameModelToJson(this); + } +} diff --git a/lib/view/home/game/model/game_model.g.dart b/lib/view/home/game/model/game_model.g.dart new file mode 100644 index 0000000..ace7b2a --- /dev/null +++ b/lib/view/home/game/model/game_model.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'game_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +GameModel _$GameModelFromJson(Map json) { + return GameModel( + sId: json['_id'] as String, + image: json['image'] as String, + name: json['name'] as String, + money: json['money'] as int, + category: json['category'] as int, + ); +} + +Map _$GameModelToJson(GameModel instance) => { + '_id': instance.sId, + 'image': instance.image, + 'name': instance.name, + 'money': instance.money, + 'category': instance.category, + }; diff --git a/lib/view/home/game/model/game_view_state.dart b/lib/view/home/game/model/game_view_state.dart new file mode 100644 index 0000000..392c7f3 --- /dev/null +++ b/lib/view/home/game/model/game_view_state.dart @@ -0,0 +1,7 @@ +enum GameViewItems { + SEARCH_BAR, + TABBAR, + SLIDER, + NEW_UPDATE_GAMES_CARD, + TOP_UPDATE_GAMES_CARD, +} diff --git a/lib/view/home/game/model/slider_model.dart b/lib/view/home/game/model/slider_model.dart new file mode 100644 index 0000000..9a5f25d --- /dev/null +++ b/lib/view/home/game/model/slider_model.dart @@ -0,0 +1,23 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:vexana/vexana.dart'; + +part 'slider_model.g.dart'; + +@JsonSerializable() +class SliderModel extends INetworkModel { + final String image; + final String text; + final String detailId; + + SliderModel({this.image, this.text, this.detailId}); + + @override + SliderModel fromJson(Map json) { + return _$SliderModelFromJson(json); + } + + @override + Map toJson() { + return _$SliderModelToJson(this); + } +} diff --git a/lib/view/home/game/model/slider_model.g.dart b/lib/view/home/game/model/slider_model.g.dart new file mode 100644 index 0000000..94fe52c --- /dev/null +++ b/lib/view/home/game/model/slider_model.g.dart @@ -0,0 +1,22 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'slider_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +SliderModel _$SliderModelFromJson(Map json) { + return SliderModel( + image: json['image'] as String, + text: json['text'] as String, + detailId: json['detailId'] as String, + ); +} + +Map _$SliderModelToJson(SliderModel instance) => + { + 'image': instance.image, + 'text': instance.text, + 'detailId': instance.detailId, + }; diff --git a/lib/view/home/game/service/IGameService.dart b/lib/view/home/game/service/IGameService.dart new file mode 100644 index 0000000..4fce539 --- /dev/null +++ b/lib/view/home/game/service/IGameService.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; +import 'package:vexana/vexana.dart'; + +import '../model/game_enums.dart'; +import '../model/game_model.dart'; +import '../model/slider_model.dart'; + +abstract class IGameService { + final INetworkManager manager; + final GlobalKey scaffoldyKey; + + IGameService(this.manager, this.scaffoldyKey); + + Future> fetchSliderItems(); + Future> fetchGameItems(GameEnum type); +} diff --git a/lib/view/home/game/service/game_service.dart b/lib/view/home/game/service/game_service.dart new file mode 100644 index 0000000..f282914 --- /dev/null +++ b/lib/view/home/game/service/game_service.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:vexana/vexana.dart'; + +import '../../../_product/_utilty/service_helper.dart'; +import '../../../_product/enum/network_route_enum.dart'; +import '../model/game_enums.dart'; +import '../model/game_model.dart'; +import '../model/slider_model.dart'; +import 'IGameService.dart'; + +class GameService extends IGameService with ServiceHelper { + GameService(INetworkManager manager, GlobalKey scaffoldyKey) : super(manager, scaffoldyKey); + + @override + Future> fetchGameItems(GameEnum type) async { + final response = await manager.fetch>(NetworkRoutes.GAME.rawValue, + urlSuffix: '/${type.index + 1}', parseModel: GameModel(), method: RequestType.GET); + showMessage(scaffoldyKey, response.error); + + await Future.delayed(Duration(seconds: 5)); + return response.data; + } + + @override + Future> fetchSliderItems() async { + final response = + await manager.fetch>(NetworkRoutes.SLIDER.rawValue, parseModel: SliderModel(), method: RequestType.GET); + showMessage(scaffoldyKey, response.error); + return response.data; + } +} diff --git a/lib/view/home/game/view/game_view.dart b/lib/view/home/game/view/game_view.dart new file mode 100644 index 0000000..5a6d7b9 --- /dev/null +++ b/lib/view/home/game/view/game_view.dart @@ -0,0 +1,108 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:kartal/kartal.dart'; +import 'package:shimmer/shimmer.dart'; + +import '../../../../core/base/view/base_widget.dart'; +import '../../../../core/init/lang/locale_keys.g.dart'; +import '../../../../core/init/network/vexana_manager.dart'; +import '../../../../product/widget/button/header_button.dart'; +import '../../../../product/widget/grid/game_grid_view.dart'; +import '../../../../product/widget/pageview/game_slider.dart'; +import '../model/game_model.dart'; +import '../model/game_view_state.dart'; +import '../model/slider_model.dart'; +import '../service/game_service.dart'; +import '../viewmodel/game_view_model.dart'; + +part './subview/game_view_cards.dart'; + +class GameView extends StatelessWidget { + final GlobalKey _scaffoldyKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + return BaseView( + viewModel: GameViewModel(GameService(VexanaManager.instance.networkManager, _scaffoldyKey)), + onModelReady: (model) { + model.setContext(context); + model.init(); + }, + onPageBuilder: (BuildContext context, GameViewModel value) => Scaffold( + key: _scaffoldyKey, + appBar: buildAppBar(context), + body: Observer(builder: (_) { + return value.isLoading + ? Center(child: CircularProgressIndicator()) + : DefaultTabController( + length: value.gameTabItems.length, + child: ListView.builder( + itemCount: GameViewItems.values.length, + itemBuilder: (context, index) { + switch (GameViewItems.values[index]) { + case GameViewItems.SEARCH_BAR: + return buildPaddingSearchBar(context); + case GameViewItems.TABBAR: + return buildTabBar(context, value); + case GameViewItems.SLIDER: + return buildSizedBoxSlider(context, value.sliderItems); + case GameViewItems.NEW_UPDATE_GAMES_CARD: + return buildColumnNewCard(value); + case GameViewItems.TOP_UPDATE_GAMES_CARD: + return buildColumnUpdate(value); + default: + throw Exception('STATE NOT FOUND'); + // return Card(child: Text("data")); + } + })); + }), + ), + ); + } + + AppBar buildAppBar(BuildContext context) { + return AppBar( + leading: Icon(Icons.rounded_corner), + actions: [IconButton(icon: Icon(Icons.camera_enhance), onPressed: () {})], + title: Text( + LocaleKeys.home_game_title.tr(), + style: context.textTheme.headline5.copyWith(color: context.colorScheme.onError, fontWeight: FontWeight.w600), + ), + ); + } + + TabBar buildTabBar(BuildContext context, GameViewModel value) { + return TabBar( + isScrollable: true, + indicatorColor: context.colorScheme.onError, + labelPadding: EdgeInsets.zero, + indicatorWeight: 3, + tabs: value.gameTabItems + .map((e) => Tab( + child: Padding(padding: context.paddingLow, child: Text(e.tr())), + )) + .toList()); + } + + Widget buildSizedBoxSlider(BuildContext context, List items) { + return Padding( + padding: context.verticalPaddingLow, + child: SizedBox( + height: context.dynamicHeight(0.32), + child: GameSlider( + sliderModel: items, + ), + ), + ); + } + + Padding buildPaddingSearchBar(BuildContext context) { + return Padding( + padding: context.paddingLow, + child: TextField( + decoration: InputDecoration(prefixIcon: Icon(Icons.search)), + ), + ); + } +} diff --git a/lib/view/home/game/view/subview/game_view_cards.dart b/lib/view/home/game/view/subview/game_view_cards.dart new file mode 100644 index 0000000..46e2804 --- /dev/null +++ b/lib/view/home/game/view/subview/game_view_cards.dart @@ -0,0 +1,44 @@ +part of '../game_view.dart'; + +extension _GameViewGamesCard on GameView { + Widget buildColumnNewCard(GameViewModel viewModel) { + return viewModel.onNewsGameItems().toBuild>( + onSuccess: (data) { + return Column( + children: [HeaderButton(titleText: LocaleKeys.home_game_newUpdate), GameGrid(models: data)], + ); + }, + loaindgWidget: SizedBox( + height: 200, + width: 100, + child: Shimmer.fromColors( + baseColor: Colors.grey, + highlightColor: Colors.grey[200], + child: ListTile( + leading: CircleAvatar(), + title: Text("data"), + subtitle: Text("data"), + ), + ), + ), + notFoundWidget: Text('data'), + onError: Text('data')); + } + + Widget buildColumnUpdate(GameViewModel viewModel) { + return viewModel.onNewsGameItems().toBuild>( + onSuccess: (data) { + return Column( + children: [ + HeaderButton(titleText: LocaleKeys.home_game_topDownload), + GameGrid( + models: data, + ) + ], + ); + }, + loaindgWidget: CircularProgressIndicator(), + notFoundWidget: Text('data'), + onError: Text('data')); + } +} diff --git a/lib/view/home/game/viewmodel/game_view_model.dart b/lib/view/home/game/viewmodel/game_view_model.dart new file mode 100644 index 0000000..b98c1c7 --- /dev/null +++ b/lib/view/home/game/viewmodel/game_view_model.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:mobx/mobx.dart'; + +import '../../../../core/base/model/base_view_model.dart'; +import '../../../../core/init/lang/locale_keys.g.dart'; +import '../model/game_enums.dart'; +import '../model/game_model.dart'; +import '../model/slider_model.dart'; +import '../service/game_service.dart'; + +part 'game_view_model.g.dart'; + +class GameViewModel = _GameViewModelBase with _$GameViewModel; + +abstract class _GameViewModelBase with Store, BaseViewModel { + final List gameTabItems = [ + LocaleKeys.home_game_tabbar_tab1, + LocaleKeys.home_game_tabbar_tab2, + LocaleKeys.home_game_tabbar_tab3, + LocaleKeys.home_game_tabbar_tab4, + LocaleKeys.home_game_tabbar_tab5 + ]; + + final GameService gameService; + + List sliderItems = []; + List newsGameItems = []; + List topGameITems = []; + _GameViewModelBase(this.gameService); + + @override + void setContext(BuildContext context) => this.context = context; + + @observable + bool isLoading = false; + + @override + void init() { + fetchSliderItems(); + } + + @action + Future fetchSliderItems() async { + changeLoading(); + final response = await gameService.fetchSliderItems(); + sliderItems = response; + changeLoading(); + } + + Future> onNewsGameItems() async { + if (newsGameItems.isNotEmpty) return newsGameItems; + final response = await gameService.fetchGameItems(GameEnum.NEW); + newsGameItems = response ?? []; + + return response; + } + + Future> onTopGameItems() async { + if (newsGameItems.isNotEmpty) return newsGameItems; + final response = await gameService.fetchGameItems(GameEnum.TOP); + newsGameItems = response ?? []; + + return response; + } + + @action + void changeLoading() { + isLoading = !isLoading; + } +} diff --git a/lib/view/home/game/viewmodel/game_view_model.g.dart b/lib/view/home/game/viewmodel/game_view_model.g.dart new file mode 100644 index 0000000..50910c0 --- /dev/null +++ b/lib/view/home/game/viewmodel/game_view_model.g.dart @@ -0,0 +1,55 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'game_view_model.dart'; + +// ************************************************************************** +// StoreGenerator +// ************************************************************************** + +// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic + +mixin _$GameViewModel on _GameViewModelBase, Store { + final _$isLoadingAtom = Atom(name: '_GameViewModelBase.isLoading'); + + @override + bool get isLoading { + _$isLoadingAtom.reportRead(); + return super.isLoading; + } + + @override + set isLoading(bool value) { + _$isLoadingAtom.reportWrite(value, super.isLoading, () { + super.isLoading = value; + }); + } + + final _$fetchSliderItemsAsyncAction = + AsyncAction('_GameViewModelBase.fetchSliderItems'); + + @override + Future fetchSliderItems() { + return _$fetchSliderItemsAsyncAction.run(() => super.fetchSliderItems()); + } + + final _$_GameViewModelBaseActionController = + ActionController(name: '_GameViewModelBase'); + + @override + void changeLoading() { + final _$actionInfo = _$_GameViewModelBaseActionController.startAction( + name: '_GameViewModelBase.changeLoading'); + try { + return super.changeLoading(); + } finally { + _$_GameViewModelBaseActionController.endAction(_$actionInfo); + } + } + + @override + String toString() { + return ''' +isLoading: ${isLoading} + '''; + } +} diff --git a/pubspec.lock b/pubspec.lock index 7ce052f..df98e9e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -22,6 +22,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.2" + archive: + dependency: transitive + description: + name: archive + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.13" args: dependency: transitive description: @@ -106,6 +113,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "7.1.0" + cached_network_image: + dependency: "direct main" + description: + name: cached_network_image + url: "https://pub.dartlang.org" + source: hosted + version: "2.5.0" characters: dependency: transitive description: @@ -244,6 +258,20 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_blurhash: + dependency: transitive + description: + name: flutter_blurhash + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.0" + flutter_cache_manager: + dependency: transitive + description: + name: flutter_cache_manager + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" flutter_localizations: dependency: transitive description: flutter @@ -287,6 +315,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.2.0" + http: + dependency: transitive + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.2" http_multi_server: dependency: transitive description: @@ -301,6 +336,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.4" + image: + dependency: transitive + description: + name: image + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.19" intl: dependency: transitive description: @@ -420,6 +462,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + octo_image: + dependency: transitive + description: + name: octo_image + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0" package_config: dependency: transitive description: @@ -553,6 +602,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.5" + rxdart: + dependency: transitive + description: + name: rxdart + url: "https://pub.dartlang.org" + source: hosted + version: "0.25.0" share: dependency: transitive description: @@ -616,6 +672,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.2.3" + shimmer: + dependency: "direct main" + description: + name: shimmer + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.2" sky_engine: dependency: transitive description: flutter @@ -635,6 +698,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0-nullsafety.2" + sqflite: + dependency: transitive + description: + name: sqflite + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.2+3" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3+1" stack_trace: dependency: transitive description: @@ -663,6 +740,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0-nullsafety.1" + synchronized: + dependency: transitive + description: + name: synchronized + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0+2" term_glyph: dependency: transitive description: @@ -733,6 +817,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.0.1+3" + uuid: + dependency: transitive + description: + name: uuid + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.2" vector_math: dependency: transitive description: @@ -790,5 +881,5 @@ packages: source: hosted version: "2.2.1" sdks: - dart: ">=2.10.0 <2.11.0" - flutter: ">=1.22.0 <2.0.0" + dart: ">=2.10.2 <2.11.0" + flutter: ">=1.22.2 <2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 3e3e794..8da1d8b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -26,6 +26,7 @@ dependencies: animations: ^1.1.2 auto_size_text: ^2.1.0 build_runner: ^1.10.0 + cached_network_image: ^2.5.0 cupertino_icons: ^0.1.3 dio: ^3.0.9 easy_localization: ^2.3.2 @@ -40,6 +41,7 @@ dependencies: pedantic: ^1.9.2 provider: ^4.3.3 shared_preferences: ^0.5.7+3 + shimmer: ^1.1.2 vexana: ^1.2.2 dev_dependencies: