diff --git a/lib/app/constants/ui.dart b/lib/app/constants/ui.dart index 2a58de662..e9d7ce4ea 100644 --- a/lib/app/constants/ui.dart +++ b/lib/app/constants/ui.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; const double kDefaultPadding = 44.0; const double kDefaultIconButtonSide = 44.0; +const double kDefaultNavHeaderTopPadding = 50.0; const TextStyle shadowStyle = TextStyle( shadows: [ diff --git a/lib/app/features/dapps/views/pages/dapps.dart b/lib/app/features/dapps/views/pages/dapps.dart index aa783823c..43079623a 100644 --- a/lib/app/features/dapps/views/pages/dapps.dart +++ b/lib/app/features/dapps/views/pages/dapps.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:ice/app/extensions/build_context.dart'; import 'package:ice/app/extensions/theme_data.dart'; @@ -14,6 +15,8 @@ class DAppsPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final GoRouter router = GoRouter.of(context); + return SingleChildScrollView( child: Container( decoration: @@ -23,8 +26,20 @@ class DAppsPage extends HookConsumerWidget { const WalletHeader(), const Featured(), const Categories(), - Apps(title: 'Highest ranked', items: featured, onPress: () {}), - Apps(title: 'Recently added', items: featured, onPress: () {}), + Apps( + title: 'Highest ranked', + items: featured, + onPress: () { + router.go('/dapps/appsList'); + }, + ), + Apps( + title: 'Recently added', + items: featured, + onPress: () { + router.go('/dapps/appsList'); + }, + ), const Favourites(), ], ), diff --git a/lib/app/features/dapps/views/pages/dapps_list/dapps_list.dart b/lib/app/features/dapps/views/pages/dapps_list/dapps_list.dart new file mode 100644 index 000000000..55c765bc8 --- /dev/null +++ b/lib/app/features/dapps/views/pages/dapps_list/dapps_list.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:ice/app/constants/ui.dart'; +import 'package:ice/app/extensions/build_context.dart'; +import 'package:ice/app/extensions/theme_data.dart'; +import 'package:ice/app/features/dapps/views/pages/mocks/mocked_apps.dart'; +import 'package:ice/app/features/dapps/views/pages/widgets/apps_collection.dart'; +import 'package:ice/app/shared/widgets/navigation_header/navigation_header.dart'; +import 'package:ice/app/shared/widgets/search/search.dart'; + +class DAppsList extends HookConsumerWidget { + const DAppsList({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final ValueNotifier searchText = useState(''); + + final List filteredApps = searchText.value.isEmpty + ? featured + : featured.where((DAppItem app) { + final String searchLower = searchText.value.toLowerCase().trim(); + final String titleLower = app.title.toLowerCase(); + + return titleLower.contains(searchLower); + }).toList(); + + return Scaffold( + body: Container( + margin: const EdgeInsets.only(top: kDefaultNavHeaderTopPadding), + width: double.infinity, + color: context.theme.appColors.secondaryBackground, + child: Stack( + children: [ + const NavigationHeader( + title: 'DeFi', + ), + Padding( + padding: const EdgeInsets.only(top: navigationHeaderHeight), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Search( + onTextChanged: (String value) => searchText.value = value, + onClearText: () => searchText.value = '', + ), + Expanded( + child: ListView.builder( + itemCount: filteredApps.length, + itemBuilder: (BuildContext context, int index) { + final DAppItem app = filteredApps[index]; + return DAppGridItem(item: app, showIsFavourite: true); + }, + ), + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/app/features/dapps/views/pages/mocks/mocked_apps.dart b/lib/app/features/dapps/views/pages/mocks/mocked_apps.dart index 390063d89..d8dc34ecc 100644 --- a/lib/app/features/dapps/views/pages/mocks/mocked_apps.dart +++ b/lib/app/features/dapps/views/pages/mocks/mocked_apps.dart @@ -4,16 +4,20 @@ class DAppItem { DAppItem({ required this.iconImage, required this.title, + this.isFavourite = false, this.description = '', this.value = 0.0, this.isVerified = false, + this.backgroundImage, }); final String iconImage; final String title; + final bool isFavourite; final String? description; final double? value; final bool? isVerified; + final String? backgroundImage; } List featured = [ @@ -23,6 +27,7 @@ List featured = [ description: 'Swap ERC-20 tokens', value: 4190.77, isVerified: true, + isFavourite: true, ), DAppItem( iconImage: Assets.images.walletOpensea.path, @@ -30,29 +35,34 @@ List featured = [ description: 'NFT marketplace', value: 3938.25, isVerified: true, + isFavourite: true, ), DAppItem( iconImage: Assets.images.wallet1inch.path, title: '1inch', description: 'DEX price optimizer', value: 2681.49, + isFavourite: true, ), DAppItem( iconImage: Assets.images.walletPanecakeswap.path, title: 'Pancakeswap', description: 'BSC decentralized exchange', value: 1348.52, + isFavourite: true, ), DAppItem( iconImage: Assets.images.walletStargate.path, title: 'Stargate', description: 'Cross-chain transaction facilitator', value: 938.25, + isFavourite: true, ), DAppItem( iconImage: Assets.images.walletLido.path, title: 'Lido', description: 'Decentralized ETH 2.0 staking', value: 497.08, + isFavourite: true, ), ]; diff --git a/lib/app/features/dapps/views/pages/mocks/mocked_featured.dart b/lib/app/features/dapps/views/pages/mocks/mocked_featured.dart index 2da4fe1f0..0c4cf886a 100644 --- a/lib/app/features/dapps/views/pages/mocks/mocked_featured.dart +++ b/lib/app/features/dapps/views/pages/mocks/mocked_featured.dart @@ -1,27 +1,14 @@ +import 'package:ice/app/features/dapps/views/pages/mocks/mocked_apps.dart'; import 'package:ice/generated/assets.gen.dart'; -class FeaturedItem { - FeaturedItem({ - required this.backgroundImage, - required this.iconImage, - required this.title, - required this.description, - }); - - final String backgroundImage; - final String iconImage; - final String title; - final String description; -} - -List featured = [ - FeaturedItem( +List featured = [ + DAppItem( backgroundImage: Assets.images.uniswapBg.path, iconImage: Assets.images.featuredUniswap.path, title: 'Uniswap wallet', description: 'Buy & trade top tokens', ), - FeaturedItem( + DAppItem( backgroundImage: Assets.images.oneInchBg.path, iconImage: Assets.images.featured1inch.path, title: '1inch', diff --git a/lib/app/features/dapps/views/pages/widgets/apps_collection.dart b/lib/app/features/dapps/views/pages/widgets/apps_collection.dart index e5b29472a..2df910709 100644 --- a/lib/app/features/dapps/views/pages/widgets/apps_collection.dart +++ b/lib/app/features/dapps/views/pages/widgets/apps_collection.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:ice/app/extensions/build_context.dart'; import 'package:ice/app/extensions/theme_data.dart'; import 'package:ice/app/features/dapps/views/pages/mocks/mocked_apps.dart'; +import 'package:ice/app/shared/widgets/favourite_icon/favorite_icon.dart'; import 'package:ice/app/utils/extensions.dart'; import 'package:ice/app/values/constants.dart'; @@ -51,8 +52,13 @@ class AppsCollection extends StatelessWidget { } class DAppGridItem extends StatelessWidget { - const DAppGridItem({super.key, required this.item}); + const DAppGridItem({ + super.key, + required this.item, + this.showIsFavourite = false, + }); final DAppItem item; + final bool showIsFavourite; @override Widget build(BuildContext context) { @@ -63,45 +69,54 @@ class DAppGridItem extends StatelessWidget { ), width: double.infinity, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Container( - width: 48, - height: 48, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(8), - ), - child: Image.asset( - item.iconImage, - width: 48, - fit: BoxFit.contain, - ), - ), - const SizedBox(width: 8), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, + Row( children: [ - Text( - item.title, - style: context.theme.appTextThemes.body.copyWith( - color: context.theme.appColors.primaryText, + Container( + width: 48, + height: 48, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), ), - ), - Text( - item.description ?? '', - style: context.theme.appTextThemes.caption3.copyWith( - color: context.theme.appColors.secondaryText, + child: Image.asset( + item.iconImage, + width: 48, + fit: BoxFit.contain, ), ), - Text( - item.value != null ? formatDouble(item.value!) : '', - style: context.theme.appTextThemes.caption3.copyWith( - color: context.theme.appColors.tertararyText, - ), + const SizedBox(width: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + item.title, + style: context.theme.appTextThemes.body.copyWith( + color: context.theme.appColors.primaryText, + ), + ), + Text( + item.description ?? '', + style: context.theme.appTextThemes.caption3.copyWith( + color: context.theme.appColors.secondaryText, + ), + ), + Text( + item.value != null ? formatDouble(item.value!) : '', + style: context.theme.appTextThemes.caption3.copyWith( + color: context.theme.appColors.tertararyText, + ), + ), + ], ), ], ), + if (showIsFavourite) + FavouriteIcon( + isFavourite: item.isFavourite, + ), ], ), ); diff --git a/lib/app/features/dapps/views/pages/widgets/featured_collection.dart b/lib/app/features/dapps/views/pages/widgets/featured_collection.dart index 465ca5e97..a73c6b614 100644 --- a/lib/app/features/dapps/views/pages/widgets/featured_collection.dart +++ b/lib/app/features/dapps/views/pages/widgets/featured_collection.dart @@ -2,12 +2,12 @@ import 'package:flutter/material.dart'; import 'package:ice/app/constants/ui.dart'; import 'package:ice/app/extensions/build_context.dart'; import 'package:ice/app/extensions/theme_data.dart'; -import 'package:ice/app/features/dapps/views/pages/mocks/mocked_featured.dart'; +import 'package:ice/app/features/dapps/views/pages/mocks/mocked_apps.dart'; class FeaturedCollection extends StatelessWidget { const FeaturedCollection({super.key, required this.items}); - final List items; + final List items; @override Widget build(BuildContext context) { @@ -23,6 +23,7 @@ class FeaturedCollection extends StatelessWidget { itemBuilder: (BuildContext context, int index) { final double leftOffset = index == 0 ? 16.0 : 8.0; final double rightOffset = index == items.length - 1 ? 16.0 : 8.0; + final String assetBg = items[index].backgroundImage ?? ''; return Container( width: itemWidth, margin: EdgeInsets.only(right: rightOffset, left: leftOffset), @@ -30,7 +31,7 @@ class FeaturedCollection extends StatelessWidget { borderRadius: BorderRadius.circular(12), image: DecorationImage( image: AssetImage( - items[index].backgroundImage, + assetBg, ), fit: BoxFit.cover, ), @@ -71,7 +72,7 @@ class FeaturedCollection extends StatelessWidget { .merge(shadowStyle), ), Text( - items[index].description, + items[index].description ?? '', style: context.theme.appTextThemes.caption3 .copyWith( color: context diff --git a/lib/app/router/app_routes.dart b/lib/app/router/app_routes.dart index cae784ecf..55f90e284 100644 --- a/lib/app/router/app_routes.dart +++ b/lib/app/router/app_routes.dart @@ -9,6 +9,7 @@ import 'package:ice/app/features/core/views/pages/error_page.dart'; import 'package:ice/app/features/core/views/pages/modal_page.dart'; import 'package:ice/app/features/core/views/pages/splash_page.dart'; import 'package:ice/app/features/dapps/views/pages/dapps.dart'; +import 'package:ice/app/features/dapps/views/pages/dapps_list/dapps_list.dart'; import 'package:ice/app/features/wallet/views/pages/inner_wallet_page.dart'; import 'package:ice/app/features/wallet/views/pages/wallet_page.dart'; import 'package:ice/app/router/views/scaffold_with_nested_navigation.dart'; @@ -96,6 +97,11 @@ class FillProfileRoute extends GoRouteData { routes: >[ TypedGoRoute( path: '/dapps', + routes: >[ + TypedGoRoute( + path: 'appsList', + ), + ], ), ], ), @@ -171,6 +177,12 @@ class DAppsBranch extends StatefulShellBranchData { static final GlobalKey $navigatorKey = shellNavigatorDAppsKey; } +class DAppsListRoute extends GoRouteData { + const DAppsListRoute(); + @override + Widget build(BuildContext context, GoRouterState state) => const DAppsList(); +} + class DAppsRoute extends GoRouteData { const DAppsRoute(); @override diff --git a/lib/app/shared/widgets/navigation_header/navigation_header.dart b/lib/app/shared/widgets/navigation_header/navigation_header.dart index ebe7eb884..4331f1f5b 100644 --- a/lib/app/shared/widgets/navigation_header/navigation_header.dart +++ b/lib/app/shared/widgets/navigation_header/navigation_header.dart @@ -8,10 +8,15 @@ const double backButtonSide = 24.0; const double backButtonPadding = 10.0; class NavigationHeader extends StatelessWidget { - const NavigationHeader({required this.title, this.showBackButton = true}); + const NavigationHeader({ + required this.title, + this.showBackButton = true, + this.onBackPress, + }); final String title; final bool showBackButton; + final VoidCallback? onBackPress; @override Widget build(BuildContext context) { @@ -28,7 +33,11 @@ class NavigationHeader extends StatelessWidget { height: backButtonSide, ), onPressed: () { - Navigator.pop(context); + if (onBackPress != null) { + onBackPress!(); + } else { + Navigator.pop(context); + } }, ) else