create auth and onBoarding and home page.

This commit is contained in:
OkaykOrhmn 2025-02-16 17:15:56 +03:30
parent cf7295f466
commit a8c8fa88b9
38 changed files with 1608 additions and 188 deletions

View File

@ -10,6 +10,7 @@
analyzer: analyzer:
errors: errors:
avoid_print: ignore avoid_print: ignore
deprecated_member_use_from_same_package: ignore
include: package:flutter_lints/flutter.yaml include: package:flutter_lints/flutter.yaml
linter: linter:

View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.79 22.75H6.21C3.47 22.75 1.25 20.52 1.25 17.78V10.37C1.25 9.00997 2.09 7.29997 3.17 6.45997L8.56 2.25997C10.18 0.999974 12.77 0.939974 14.45 2.11997L20.63 6.44997C21.82 7.27997 22.75 9.05997 22.75 10.51V17.79C22.75 20.52 20.53 22.75 17.79 22.75ZM9.48 3.43997L4.09 7.63997C3.38 8.19997 2.75 9.46997 2.75 10.37V17.78C2.75 19.69 4.3 21.25 6.21 21.25H17.79C19.7 21.25 21.25 19.7 21.25 17.79V10.51C21.25 9.54997 20.56 8.21997 19.77 7.67997L13.59 3.34997C12.45 2.54997 10.57 2.58997 9.48 3.43997Z" fill="#2A2926"/>
<path d="M12 18.75C11.59 18.75 11.25 18.41 11.25 18V15C11.25 14.59 11.59 14.25 12 14.25C12.41 14.25 12.75 14.59 12.75 15V18C12.75 18.41 12.41 18.75 12 18.75Z" fill="#2A2926"/>
</svg>

After

Width:  |  Height:  |  Size: 801 B

View File

@ -0,0 +1,7 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.3799 21.13C20.1799 21.13 19.9899 21.05 19.8499 20.91L13.4699 14.53C13.1799 14.24 13.1799 13.76 13.4699 13.47L21.2 5.74001C21.39 5.55001 21.6799 5.48001 21.9399 5.55001C22.1999 5.63001 22.3999 5.84001 22.4599 6.10001C22.6499 6.95001 22.7499 7.90001 22.7499 9.00001V15C22.7499 17.77 22.1699 19.64 20.9099 20.91C20.7699 21.05 20.5599 21.08 20.3799 21.13ZM15.0599 14L20.3199 19.26C20.9499 18.29 21.2499 16.91 21.2499 15V9.00001C21.2499 8.59001 21.2399 8.21001 21.2099 7.85001L15.0599 14Z" fill="#2A2926"/>
<path d="M6.26999 22.48C6.20999 22.48 6.16001 22.47 6.10001 22.46C2.79001 21.7 1.25 19.33 1.25 15V9C1.25 3.57 3.57 1.25 9 1.25H15C19.33 1.25 21.7 2.79001 22.46 6.10001C22.52 6.35001 22.44 6.62 22.26 6.8L6.79999 22.26C6.65999 22.4 6.46999 22.48 6.26999 22.48ZM9 2.75C4.39 2.75 2.75 4.39 2.75 9V15C2.75 18.47 3.71001 20.21 6.04001 20.9L20.89 6.05C20.21 3.72 18.46 2.75999 14.99 2.75999H9V2.75Z" fill="#2A2926"/>
<path d="M14.9999 22.7499H8.99989C7.89989 22.7499 6.95989 22.6599 6.09989 22.4599C5.82989 22.3999 5.61987 22.1999 5.54987 21.9399C5.46987 21.6799 5.54988 21.3999 5.73988 21.1999L13.4699 13.4699C13.7599 13.1799 14.2399 13.1799 14.5299 13.4699L20.9099 19.8499C21.0499 19.9899 21.1299 20.1799 21.1299 20.3799C21.1299 20.5799 21.0499 20.7699 20.9099 20.9099C19.6399 22.1699 17.7699 22.7499 14.9999 22.7499ZM7.84989 21.2099C8.20989 21.2399 8.58989 21.2499 8.99989 21.2499H14.9999C16.9199 21.2499 18.2899 20.9499 19.2599 20.3199L13.9999 15.0599L7.84989 21.2099Z" fill="#2A2926"/>
<path d="M9.11994 13.31C8.48994 13.31 7.85993 13.08 7.35993 12.61C5.76993 11.1 5.12996 9.44005 5.50996 7.82005C5.88996 6.16005 7.33994 5.04004 9.11994 5.04004C10.8999 5.04004 12.35 6.16005 12.73 7.82005C13.1 9.45005 12.4599 11.1 10.8699 12.61C10.3799 13.07 9.74994 13.31 9.11994 13.31ZM6.96995 8.15004C6.64995 9.51004 7.56994 10.7301 8.39994 11.5201C8.80994 11.91 9.43994 11.91 9.83994 11.5201C10.6599 10.7401 11.5799 9.52004 11.2699 8.15004C10.9999 6.96004 9.93994 6.53004 9.11994 6.53004C8.29994 6.53004 7.24995 6.96004 6.96995 8.15004Z" fill="#2A2926"/>
<path d="M9.1499 9.48999C8.5999 9.48999 8.1499 9.03999 8.1499 8.48999C8.1499 7.93999 8.5899 7.48999 9.1499 7.48999H9.15991C9.70991 7.48999 10.1599 7.93999 10.1599 8.48999C10.1599 9.03999 9.6999 9.48999 9.1499 9.48999Z" fill="#2A2926"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 10.52C11.59 10.52 11.25 10.18 11.25 9.77V6.44C11.25 6.03 11.59 5.69 12 5.69C12.41 5.69 12.75 6.03 12.75 6.44V9.77C12.75 10.19 12.41 10.52 12 10.52Z" fill="#292D32"/>
<path d="M12.0199 20.35C9.43987 20.35 6.86987 19.94 4.41987 19.12C3.50987 18.82 2.81987 18.17 2.51987 17.35C2.21987 16.53 2.31987 15.59 2.80987 14.77L4.07987 12.65C4.35987 12.18 4.60987 11.3 4.60987 10.75V8.65001C4.60987 4.56001 7.92987 1.24001 12.0199 1.24001C16.1099 1.24001 19.4299 4.56001 19.4299 8.65001V10.75C19.4299 11.29 19.6799 12.18 19.9599 12.65L21.2299 14.77C21.6999 15.55 21.7799 16.48 21.4699 17.33C21.1599 18.18 20.4799 18.83 19.6199 19.12C17.1699 19.95 14.5999 20.35 12.0199 20.35ZM12.0199 2.75001C8.75987 2.75001 6.10987 5.40001 6.10987 8.66001V10.76C6.10987 11.57 5.78987 12.74 5.36987 13.43L4.09987 15.56C3.83987 15.99 3.77987 16.45 3.92987 16.85C4.07987 17.25 4.41987 17.55 4.89987 17.71C9.49987 19.24 14.5599 19.24 19.1599 17.71C19.5899 17.57 19.9199 17.25 20.0699 16.83C20.2299 16.41 20.1799 15.95 19.9499 15.56L18.6799 13.44C18.2599 12.75 17.9399 11.58 17.9399 10.77V8.67001C17.9299 5.40001 15.2799 2.75001 12.0199 2.75001Z" fill="#292D32"/>
<path d="M11.9999 22.9C10.9299 22.9 9.87992 22.46 9.11992 21.7C8.35992 20.94 7.91992 19.89 7.91992 18.82H9.41992C9.41992 19.5 9.69992 20.16 10.1799 20.64C10.6599 21.12 11.3199 21.4 11.9999 21.4C13.4199 21.4 14.5799 20.24 14.5799 18.82H16.0799C16.0799 21.07 14.2499 22.9 11.9999 22.9Z" fill="#292D32"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.47096 0.124023C12.6322 0.124023 16.0094 3.50123 16.0094 7.66243C16.0094 11.8236 12.6322 15.2008 8.47096 15.2008C4.30976 15.2008 0.932556 11.8236 0.932556 7.66243C0.932556 3.50123 4.30976 0.124023 8.47096 0.124023ZM8.47096 13.5256C11.71 13.5256 14.3342 10.9014 14.3342 7.66243C14.3342 4.42259 11.71 1.79922 8.47096 1.79922C5.23112 1.79922 2.60776 4.42259 2.60776 7.66243C2.60776 10.9014 5.23112 13.5256 8.47096 13.5256ZM15.578 13.5851L17.9476 15.9538L16.7624 17.139L14.3936 14.7695L15.578 13.5851Z" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 630 B

View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 15.75C9.93 15.75 8.25 14.07 8.25 12C8.25 9.93 9.93 8.25 12 8.25C14.07 8.25 15.75 9.93 15.75 12C15.75 14.07 14.07 15.75 12 15.75ZM12 9.75C10.76 9.75 9.75 10.76 9.75 12C9.75 13.24 10.76 14.25 12 14.25C13.24 14.25 14.25 13.24 14.25 12C14.25 10.76 13.24 9.75 12 9.75Z" fill="#2A2926"/>
<path d="M15.21 22.19C15 22.19 14.79 22.16 14.58 22.11C13.96 21.94 13.44 21.55 13.11 21L12.99 20.8C12.4 19.78 11.59 19.78 11 20.8L10.89 20.99C10.56 21.55 10.04 21.95 9.42 22.11C8.79 22.28 8.14 22.19 7.59 21.86L5.87 20.87C5.26 20.52 4.82 19.95 4.63 19.26C4.45 18.57 4.54 17.86 4.89 17.25C5.18 16.74 5.26 16.28 5.09 15.99C4.92 15.7 4.49 15.53 3.9 15.53C2.44 15.53 1.25 14.34 1.25 12.88V11.12C1.25 9.66004 2.44 8.47004 3.9 8.47004C4.49 8.47004 4.92 8.30004 5.09 8.01004C5.26 7.72004 5.19 7.26004 4.89 6.75004C4.54 6.14004 4.45 5.42004 4.63 4.74004C4.81 4.05004 5.25 3.48004 5.87 3.13004L7.6 2.14004C8.73 1.47004 10.22 1.86004 10.9 3.01004L11.02 3.21004C11.61 4.23004 12.42 4.23004 13.01 3.21004L13.12 3.02004C13.8 1.86004 15.29 1.47004 16.43 2.15004L18.15 3.14004C18.76 3.49004 19.2 4.06004 19.39 4.75004C19.57 5.44004 19.48 6.15004 19.13 6.76004C18.84 7.27004 18.76 7.73004 18.93 8.02004C19.1 8.31004 19.53 8.48004 20.12 8.48004C21.58 8.48004 22.77 9.67004 22.77 11.13V12.89C22.77 14.35 21.58 15.54 20.12 15.54C19.53 15.54 19.1 15.71 18.93 16C18.76 16.29 18.83 16.75 19.13 17.26C19.48 17.87 19.58 18.59 19.39 19.27C19.21 19.96 18.77 20.53 18.15 20.88L16.42 21.87C16.04 22.08 15.63 22.19 15.21 22.19ZM12 18.49C12.89 18.49 13.72 19.05 14.29 20.04L14.4 20.23C14.52 20.44 14.72 20.59 14.96 20.65C15.2 20.71 15.44 20.68 15.64 20.56L17.37 19.56C17.63 19.41 17.83 19.16 17.91 18.86C17.99 18.56 17.95 18.25 17.8 17.99C17.23 17.01 17.16 16 17.6 15.23C18.04 14.46 18.95 14.02 20.09 14.02C20.73 14.02 21.24 13.51 21.24 12.87V11.11C21.24 10.48 20.73 9.96004 20.09 9.96004C18.95 9.96004 18.04 9.52004 17.6 8.75004C17.16 7.98004 17.23 6.97004 17.8 5.99004C17.95 5.73004 17.99 5.42004 17.91 5.12004C17.83 4.82004 17.64 4.58004 17.38 4.42004L15.65 3.43004C15.22 3.17004 14.65 3.32004 14.39 3.76004L14.28 3.95004C13.71 4.94004 12.88 5.50004 11.99 5.50004C11.1 5.50004 10.27 4.94004 9.7 3.95004L9.59 3.75004C9.34 3.33004 8.78 3.18004 8.35 3.43004L6.62 4.43004C6.36 4.58004 6.16 4.83004 6.08 5.13004C6 5.43004 6.04 5.74004 6.19 6.00004C6.76 6.98004 6.83 7.99004 6.39 8.76004C5.95 9.53004 5.04 9.97004 3.9 9.97004C3.26 9.97004 2.75 10.48 2.75 11.12V12.88C2.75 13.51 3.26 14.03 3.9 14.03C5.04 14.03 5.95 14.47 6.39 15.24C6.83 16.01 6.76 17.02 6.19 18C6.04 18.26 6 18.57 6.08 18.87C6.16 19.17 6.35 19.41 6.61 19.57L8.34 20.56C8.55 20.69 8.8 20.72 9.03 20.66C9.27 20.6 9.47 20.44 9.6 20.23L9.71 20.04C10.28 19.06 11.11 18.49 12 18.49Z" fill="#2A2926"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.1596 11.62C12.1296 11.62 12.1096 11.62 12.0796 11.62C12.0296 11.61 11.9596 11.61 11.8996 11.62C8.99957 11.53 6.80957 9.25 6.80957 6.44C6.80957 3.58 9.13957 1.25 11.9996 1.25C14.8596 1.25 17.1896 3.58 17.1896 6.44C17.1796 9.25 14.9796 11.53 12.1896 11.62C12.1796 11.62 12.1696 11.62 12.1596 11.62ZM11.9996 2.75C9.96957 2.75 8.30957 4.41 8.30957 6.44C8.30957 8.44 9.86957 10.05 11.8596 10.12C11.9096 10.11 12.0496 10.11 12.1796 10.12C14.1396 10.03 15.6796 8.42 15.6896 6.44C15.6896 4.41 14.0296 2.75 11.9996 2.75Z" fill="#2A2926"/>
<path d="M12.1696 22.55C10.2096 22.55 8.23961 22.05 6.74961 21.05C5.35961 20.13 4.59961 18.87 4.59961 17.5C4.59961 16.13 5.35961 14.86 6.74961 13.93C9.74961 11.94 14.6096 11.94 17.5896 13.93C18.9696 14.85 19.7396 16.11 19.7396 17.48C19.7396 18.85 18.9796 20.12 17.5896 21.05C16.0896 22.05 14.1296 22.55 12.1696 22.55ZM7.57961 15.19C6.61961 15.83 6.09961 16.65 6.09961 17.51C6.09961 18.36 6.62961 19.18 7.57961 19.81C10.0696 21.48 14.2696 21.48 16.7596 19.81C17.7196 19.17 18.2396 18.35 18.2396 17.49C18.2396 16.64 17.7096 15.82 16.7596 15.19C14.2696 13.53 10.0696 13.53 7.57961 15.19Z" fill="#2A2926"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

View File

@ -7,7 +7,24 @@
// ignore_for_file: type=lint // ignore_for_file: type=lint
// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use // ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_svg/flutter_svg.dart' as _svg;
import 'package:vector_graphics/vector_graphics.dart' as _vg;
class $AssetsIconGen {
const $AssetsIconGen();
/// Directory path: assets/icon/outline
$AssetsIconOutlineGen get outline => const $AssetsIconOutlineGen();
}
class $AssetsImageGen {
const $AssetsImageGen();
/// Directory path: assets/image/onboarding
$AssetsImageOnboardingGen get onboarding => const $AssetsImageOnboardingGen();
}
class $AssetsLogoGen { class $AssetsLogoGen {
const $AssetsLogoGen(); const $AssetsLogoGen();
@ -20,9 +37,80 @@ class $AssetsLogoGen {
List<AssetGenImage> get values => [appLogo]; List<AssetGenImage> get values => [appLogo];
} }
class $AssetsIconOutlineGen {
const $AssetsIconOutlineGen();
/// File path: assets/icon/outline/home.svg
SvgGenImage get home =>
const SvgGenImage('assets/icon/outline/home.svg', size: Size(24.0, 24.0));
/// File path: assets/icon/outline/map.svg
SvgGenImage get map =>
const SvgGenImage('assets/icon/outline/map.svg', size: Size(24.0, 24.0));
/// File path: assets/icon/outline/notification-bing.svg
SvgGenImage get notificationBing => const SvgGenImage(
'assets/icon/outline/notification-bing.svg',
size: Size(24.0, 24.0),
);
/// File path: assets/icon/outline/search.svg
SvgGenImage get search => const SvgGenImage(
'assets/icon/outline/search.svg',
size: Size(18.0, 18.0),
);
/// File path: assets/icon/outline/setting.svg
SvgGenImage get setting => const SvgGenImage(
'assets/icon/outline/setting.svg',
size: Size(24.0, 24.0),
);
/// File path: assets/icon/outline/user.svg
SvgGenImage get user =>
const SvgGenImage('assets/icon/outline/user.svg', size: Size(24.0, 24.0));
/// List of all assets
List<SvgGenImage> get values => [
home,
map,
notificationBing,
search,
setting,
user,
];
}
class $AssetsImageOnboardingGen {
const $AssetsImageOnboardingGen();
/// File path: assets/image/onboarding/banner-1.png
AssetGenImage get banner1 => const AssetGenImage(
'assets/image/onboarding/banner-1.png',
size: Size(888.0, 879.0),
);
/// File path: assets/image/onboarding/banner-2.png
AssetGenImage get banner2 => const AssetGenImage(
'assets/image/onboarding/banner-2.png',
size: Size(888.0, 879.0),
);
/// File path: assets/image/onboarding/banner-3.png
AssetGenImage get banner3 => const AssetGenImage(
'assets/image/onboarding/banner-3.png',
size: Size(888.0, 879.0),
);
/// List of all assets
List<AssetGenImage> get values => [banner1, banner2, banner3];
}
class Assets { class Assets {
const Assets._(); const Assets._();
static const $AssetsIconGen icon = $AssetsIconGen();
static const $AssetsImageGen image = $AssetsImageGen();
static const $AssetsLogoGen logo = $AssetsLogoGen(); static const $AssetsLogoGen logo = $AssetsLogoGen();
} }
@ -95,3 +183,75 @@ class AssetGenImage {
String get keyName => _assetName; String get keyName => _assetName;
} }
class SvgGenImage {
const SvgGenImage(this._assetName, {this.size, this.flavors = const {}})
: _isVecFormat = false;
const SvgGenImage.vec(this._assetName, {this.size, this.flavors = const {}})
: _isVecFormat = true;
final String _assetName;
final Size? size;
final Set<String> flavors;
final bool _isVecFormat;
_svg.SvgPicture svg({
Key? key,
bool matchTextDirection = false,
AssetBundle? bundle,
String? package,
double? width,
double? height,
BoxFit fit = BoxFit.contain,
AlignmentGeometry alignment = Alignment.center,
bool allowDrawingOutsideViewBox = false,
WidgetBuilder? placeholderBuilder,
String? semanticsLabel,
bool excludeFromSemantics = false,
_svg.SvgTheme? theme,
ColorFilter? colorFilter,
Clip clipBehavior = Clip.hardEdge,
@deprecated Color? color,
@deprecated BlendMode colorBlendMode = BlendMode.srcIn,
@deprecated bool cacheColorFilter = false,
}) {
final _svg.BytesLoader loader;
if (_isVecFormat) {
loader = _vg.AssetBytesLoader(
_assetName,
assetBundle: bundle,
packageName: package,
);
} else {
loader = _svg.SvgAssetLoader(
_assetName,
assetBundle: bundle,
packageName: package,
theme: theme,
);
}
return _svg.SvgPicture(
loader,
key: key,
matchTextDirection: matchTextDirection,
width: width,
height: height,
fit: fit,
alignment: alignment,
allowDrawingOutsideViewBox: allowDrawingOutsideViewBox,
placeholderBuilder: placeholderBuilder,
semanticsLabel: semanticsLabel,
excludeFromSemantics: excludeFromSemantics,
colorFilter:
colorFilter ??
(color == null ? null : ColorFilter.mode(color, colorBlendMode)),
clipBehavior: clipBehavior,
cacheColorFilter: cacheColorFilter,
);
}
String get path => _assetName;
String get keyName => _assetName;
}

View File

@ -1,35 +1,78 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:proxibuy/presentation/ui/screens/home/details_page.dart'; import 'package:proxibuy/presentation/providers/cubit/user_info_cubit.dart';
import 'package:proxibuy/presentation/ui/screens/auth/auth_page.dart';
import 'package:proxibuy/presentation/ui/screens/home/home_page.dart'; import 'package:proxibuy/presentation/ui/screens/home/home_page.dart';
class AppRouter { class AppRouter {
static final home = '/'; static final initial = '/';
static final details = '/details'; static final home = '/home';
static final explore = '/explore';
static final setting = '/settings';
static final GlobalKey<NavigatorState> navigatorKey = static final GlobalKey<NavigatorState> navigatorKey =
GlobalKey<NavigatorState>(); GlobalKey<NavigatorState>();
GoRouter createRouter() { GoRouter createRouter = GoRouter(
return GoRouter( navigatorKey: navigatorKey,
navigatorKey: navigatorKey, initialLocation: home,
routes: [ routes: [
// Define your routes here GoRoute(
GoRoute( path: home,
path: '/', builder: (BuildContext context, GoRouterState state) {
builder: (BuildContext context, GoRouterState state) { return BlocConsumer<UserInfoCubit, UserInfoState>(
return const MyHomePage(); listener: (context, state) {
}, if (state is UserInfoSuccess || state is UserInfoFail) {
routes: <RouteBase>[ FlutterNativeSplash.remove();
GoRoute( }
path: details, },
builder: (BuildContext context, GoRouterState state) { builder: (context, state) {
print(state.uri.queryParameters); if (state is UserInfoSuccess) {
return const DetailsPage(); return const HomePage();
}, } else {
), return const AuthPage();
], }
), },
], );
); },
} routes: <RouteBase>[],
),
// StatefulShellRoute.indexedStack(
// builder: (context, state, navigationShell) =>
// BlocBuilder<UserInfoCubit, UserInfoState>(
// builder: (context, state) {
// if (state is UserInfoSuccess) {
// return CustomBottomNav(child: navigationShell);
// } else {
// return AuthPage();
// }
// },
// ),
// branches: [
// StatefulShellBranch(routes: [
// GoRoute(
// path: initial,
// builder: (context, state) {
// return HomeScreen();
// },
// ),
// ]),
// StatefulShellBranch(routes: [
// GoRoute(
// path: explore,
// builder: (context, state) => ExploreScreen(),
// ),
// ]),
// StatefulShellBranch(routes: [
// GoRoute(
// path: setting,
// builder: (context, state) => SettingScreen(),
// ),
// ]),
// ],
// ),
],
);
} }

View File

@ -0,0 +1,3 @@
class ApiRoutes {
static final userInfo = '/userinfo';
}

View File

@ -0,0 +1,94 @@
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
final ApiService apiService = ApiService();
class ApiService {
final keycloak = 'https://keycloak.liara.run/';
final clientID = 'frontend';
final realm = 'lba';
late Dio _dio;
void setAuthToken(String token) {
_dio.options.headers['Authorization'] = 'Bearer $token';
}
ApiService() {
_dio = Dio(
BaseOptions(
baseUrl:
'$keycloak/$realm/protocol/openid-connect', // 🔹 Set your API base URL
connectTimeout: const Duration(seconds: 10), // Timeout settings
receiveTimeout: const Duration(seconds: 10),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
),
)..interceptors.add(PrettyDioLogger(
enabled: kDebugMode,
));
}
/// 🔹 Handle GET requests
Future<dynamic> get(String endpoint, {Map<String, dynamic>? params}) async {
try {
Response response = await _dio.get(endpoint, queryParameters: params);
return response.data;
} catch (e) {
_handleError(e);
}
}
/// 🔹 Handle POST requests
Future<dynamic> post(String endpoint, {dynamic data}) async {
try {
Response response = await _dio.post(endpoint, data: data);
return response.data;
} catch (e) {
_handleError(e);
}
}
/// 🔹 Handle PUT requests
Future<dynamic> put(String endpoint, {dynamic data}) async {
try {
Response response = await _dio.put(endpoint, data: data);
return response.data;
} catch (e) {
_handleError(e);
}
}
/// 🔹 Handle DELETE requests
Future<dynamic> delete(String endpoint, {dynamic data}) async {
try {
Response response = await _dio.delete(endpoint, data: data);
return response.data;
} catch (e) {
_handleError(e);
}
}
/// 🔹 Error handling
void _handleError(dynamic error) {
if (error is DioException) {
switch (error.type) {
case DioExceptionType.connectionTimeout:
throw Exception("Connection timeout, please try again.");
case DioExceptionType.receiveTimeout:
throw Exception("Receive timeout, please try again.");
case DioExceptionType.badResponse:
throw Exception("Server error: ${error.response?.statusCode}");
case DioExceptionType.cancel:
throw Exception("Request canceled.");
default:
throw Exception("Unexpected error occurred.");
}
} else {
throw Exception("Unknown error: $error");
}
}
}

View File

@ -0,0 +1,14 @@
import 'package:universal_html/html.dart';
class NotificationServiceWeb {
static void showNotification() {
if (Notification.supported) {
Notification('Handbags & Wallets | White Elm',
body: 'Leander, Texas (8.7 Km away)',
icon:
'https://s3-alpha-sig.figma.com/img/652b/471b/a8059768c434c7abf4cfdc3c879e9293?Expires=1740355200&Key-Pair-Id=APKAQ4GOSFWCW27IBOMQ&Signature=EVePXMyyhpSNgasV3uH1d5qMrPSkjn5KzpSxFoySpBs9uzU1aNHQoab9G9iOliFDRRSZ62RggnoomQ52og0UTPnMBUG6c-5GKT~f-C~sGnqppDoNf9UhhCBXpbE-BJYvkdXyuc7YmcdVyljgcWfChHANQL~nz5egKp-nyoL6mRfa6z1AH5IVaD-hMcA~cgcM2iRiJxddCciGyRNyr0cS8XWilO55sA1blB-q2Bp9sKZJCezw5Nhn2S8SLSVnDrNjBAJv3ph4ArsCxHH5DhCNmPiDA~AT3LOz~lQwVVJZhkIEAK6zS1TpvrsPPfNp7gAgr4MG3GM2CGl19AwrSoxDdw__');
} else {
print('Notifications are not supported on this browser.');
}
}
}

View File

@ -0,0 +1,26 @@
import 'package:flutter/services.dart';
class NotificationSrviceAndroid {
static const platform = MethodChannel('custom_notification');
NotificationSrviceAndroid() {
platform.setMethodCallHandler(_handleMethod);
}
Future<void> _handleMethod(MethodCall call) async {
print(' method ${call.method}');
}
static Future<void> showCustomNotification() async {
try {
await platform.invokeMethod('showCustomNotification', {
'title': 'Handbags & Wallets | White Elm',
'content': 'Leander, Texas (8.7 Km away)',
'imageUrl':
'https://s3-alpha-sig.figma.com/img/652b/471b/a8059768c434c7abf4cfdc3c879e9293?Expires=1740355200&Key-Pair-Id=APKAQ4GOSFWCW27IBOMQ&Signature=EVePXMyyhpSNgasV3uH1d5qMrPSkjn5KzpSxFoySpBs9uzU1aNHQoab9G9iOliFDRRSZ62RggnoomQ52og0UTPnMBUG6c-5GKT~f-C~sGnqppDoNf9UhhCBXpbE-BJYvkdXyuc7YmcdVyljgcWfChHANQL~nz5egKp-nyoL6mRfa6z1AH5IVaD-hMcA~cgcM2iRiJxddCciGyRNyr0cS8XWilO55sA1blB-q2Bp9sKZJCezw5Nhn2S8SLSVnDrNjBAJv3ph4ArsCxHH5DhCNmPiDA~AT3LOz~lQwVVJZhkIEAK6zS1TpvrsPPfNp7gAgr4MG3GM2CGl19AwrSoxDdw__',
});
} on PlatformException catch (e) {
print("Failed to show notification: '${e.message}'.");
}
}
}

View File

@ -0,0 +1,40 @@
class UserInfoModel {
String? sub;
bool? emailVerified;
String? name;
String? preferredUsername;
String? givenName;
String? familyName;
String? email;
UserInfoModel(
{this.sub,
this.emailVerified,
this.name,
this.preferredUsername,
this.givenName,
this.familyName,
this.email});
UserInfoModel.fromJson(Map<String, dynamic> json) {
sub = json['sub'];
emailVerified = json['email_verified'];
name = json['name'];
preferredUsername = json['preferred_username'];
givenName = json['given_name'];
familyName = json['family_name'];
email = json['email'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['sub'] = sub;
data['email_verified'] = emailVerified;
data['name'] = name;
data['preferred_username'] = preferredUsername;
data['given_name'] = givenName;
data['family_name'] = familyName;
data['email'] = email;
return data;
}
}

View File

@ -0,0 +1,8 @@
import 'package:proxibuy/core/gen/assets.gen.dart';
class OnboardingBannerModel {
final String desc;
final AssetGenImage image;
OnboardingBannerModel({required this.desc, required this.image});
}

View File

@ -0,0 +1,8 @@
import 'package:proxibuy/core/gen/assets.gen.dart';
class ScreenModel {
final String title;
final SvgGenImage icon;
ScreenModel({required this.title, required this.icon});
}

View File

@ -0,0 +1,16 @@
import 'package:proxibuy/core/services/api/api_routes.dart';
import 'package:proxibuy/core/services/api/api_service.dart';
import 'package:proxibuy/data/models/auth/user_info_model.dart';
class AuthRepository {
static Future<UserInfoModel?> fetchUserInfo() async {
try {
var response = await apiService.get(ApiRoutes.userInfo);
print("Users: $response");
return UserInfoModel.fromJson(response);
} catch (e) {
print("Error: $e");
rethrow;
}
}
}

View File

@ -8,6 +8,7 @@ class SharedPreferencesHelper {
} }
static const String themeMode = 'theme-mode'; static const String themeMode = 'theme-mode';
static const String token = 'auth_token';
} }
class ThemeModeStorage { class ThemeModeStorage {
@ -26,3 +27,20 @@ class ThemeModeStorage {
prefs.remove(SharedPreferencesHelper.themeMode); prefs.remove(SharedPreferencesHelper.themeMode);
} }
} }
class AuthTokenStorage {
static String getToken() {
final prefs = SharedPreferencesHelper.preferences;
return prefs.getString(SharedPreferencesHelper.token) ?? "";
}
static void setToken(String token) {
final prefs = SharedPreferencesHelper.preferences;
prefs.setString(SharedPreferencesHelper.token, token);
}
static void clearToken() {
final prefs = SharedPreferencesHelper.preferences;
prefs.remove(SharedPreferencesHelper.token);
}
}

View File

@ -4,6 +4,7 @@ import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:proxibuy/core/routes/app_router.dart'; import 'package:proxibuy/core/routes/app_router.dart';
import 'package:proxibuy/data/storage/shared_preferences_helper.dart'; import 'package:proxibuy/data/storage/shared_preferences_helper.dart';
import 'package:proxibuy/presentation/providers/cubit/them_mode_cubit.dart'; import 'package:proxibuy/presentation/providers/cubit/them_mode_cubit.dart';
import 'package:proxibuy/presentation/providers/cubit/user_info_cubit.dart';
import 'package:proxibuy/presentation/ui/theme/theme.dart'; import 'package:proxibuy/presentation/ui/theme/theme.dart';
import 'package:url_strategy/url_strategy.dart'; import 'package:url_strategy/url_strategy.dart';
@ -15,7 +16,9 @@ void main() async {
runApp(MultiBlocProvider( runApp(MultiBlocProvider(
providers: [ providers: [
BlocProvider<ThemModeCubit>(create: (context) => ThemModeCubit()) BlocProvider<ThemModeCubit>(create: (context) => ThemModeCubit()),
BlocProvider<UserInfoCubit>(
create: (context) => UserInfoCubit()..getUserInfo()),
], ],
child: const MyApp(), child: const MyApp(),
)); ));
@ -38,13 +41,13 @@ class _MyAppState extends State<MyApp> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<ThemModeCubit, ThemeMode>( return BlocBuilder<ThemModeCubit, ThemeMode>(
builder: (context, state) { builder: (context, themeState) {
return MaterialApp.router( return MaterialApp.router(
routerConfig: AppRouter().createRouter(), routerConfig: AppRouter().createRouter,
title: 'Proxibuy', title: 'Proxibuy',
theme: appTheme, theme: appTheme,
darkTheme: darkTheme, darkTheme: darkTheme,
themeMode: state, themeMode: themeState,
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
); );
}, },

View File

@ -0,0 +1,27 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:proxibuy/data/models/auth/user_info_model.dart';
part 'user_info_state.dart';
class UserInfoCubit extends Cubit<UserInfoState> {
UserInfoCubit() : super(UserInfoInitial());
void getUserInfo() async {
emit(UserInfoLoadng());
try {
// final response = await AuthRepository.fetchUserInfo();
emit(UserInfoSuccess(
user: UserInfoModel.fromJson({
"sub": "f39c8216-388a-44e1-85a8-4f996e66e604",
"email_verified": true,
"name": "demo demo",
"preferred_username": "demo",
"given_name": "demo",
"family_name": "demo",
"email": "demo@gmail.com"
})));
} catch (e) {
emit(UserInfoFail(message: e));
}
}
}

View File

@ -0,0 +1,19 @@
part of 'user_info_cubit.dart';
sealed class UserInfoState {}
final class UserInfoInitial extends UserInfoState {}
final class UserInfoLoadng extends UserInfoState {}
final class UserInfoSuccess extends UserInfoState {
final UserInfoModel user;
UserInfoSuccess({required this.user});
}
final class UserInfoFail extends UserInfoState {
final Object? message;
UserInfoFail({required this.message});
}

View File

@ -0,0 +1,226 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:proxibuy/core/gen/assets.gen.dart';
import 'package:proxibuy/core/routes/app_router.dart';
import 'package:proxibuy/core/utils/empty_space.dart';
import 'package:proxibuy/data/models/onboarding_banner_model.dart';
import 'package:proxibuy/presentation/providers/cubit/them_mode_cubit.dart';
import 'package:proxibuy/presentation/ui/theme/responsive.dart';
import 'package:proxibuy/presentation/ui/widgets/carousel/carousel_slider_widget.dart';
import 'package:proxibuy/presentation/ui/widgets/edit_text/phone_number_input.dart';
class AuthPage extends StatefulWidget {
const AuthPage({super.key});
@override
State<AuthPage> createState() => _AuthPageState();
}
class _AuthPageState extends State<AuthPage> {
bool onBoarding = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Auth',
style: Theme.of(context).textTheme.titleLarge,
),
backgroundColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
actions: [
IconButton(
icon: Icon(Icons.brightness_6),
onPressed: () {
context.read<ThemModeCubit>().changeTheme();
},
),
],
),
body: Responsive(context).builder(
mobile: onBoarding
? carousle(
withNavs: true, autoPlay: false, enableInfiniteScroll: false)
: auth(context),
tablet: Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(50.0),
child: carousle(
withNavs: false,
autoPlay: true,
enableInfiniteScroll: true,
height: MediaQuery.sizeOf(context).height * 0.8),
),
),
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 50),
child: auth(context)))
],
),
desktop: Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(80.0),
child: carousle(
withNavs: false,
autoPlay: true,
enableInfiniteScroll: true,
height: MediaQuery.sizeOf(context).height * 0.8),
),
),
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 160),
child: auth(context)))
],
)),
);
}
Padding auth(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Center(
child: FlutterLogo(
size: 120,
),
),
Text('Proxibuy Geofencing marketing'),
36.h,
Row(
children: [
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text('Login'),
Text('Please enter your phone number')
],
),
],
),
36.h,
PhoneNumberInput(),
36.h,
SizedBox(
width: double.infinity,
height: 46,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(64),
),
),
onPressed: () async {
context.go(AppRouter.initial);
},
child: Text('Get OTP'),
),
),
24.h,
Row(
children: [
Expanded(
child: Divider(
color: Theme.of(context).colorScheme.onSurface,
)),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Text('Or continue with'),
),
Expanded(child: Builder(builder: (context) {
return Divider(
color: Theme.of(context).colorScheme.onSurface,
);
}))
],
),
24.h,
SizedBox(
width: double.infinity,
height: 46,
child: OutlinedButton(
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(64),
),
side: BorderSide(
width: 2,
color: Theme.of(context).colorScheme.onSurface)),
onPressed: () async {},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Image.network(
'https://img.icons8.com/?size=100&id=110560&format=png&color=000000',
width: 32, // Adjust width
height: 32, // Adjust height
),
),
Text(
'Login with google',
style: Theme.of(context).textTheme.labelLarge,
),
],
)),
),
],
),
);
}
CarouselSliderWidget<OnboardingBannerModel> carousle(
{required final bool withNavs,
required final bool autoPlay,
required final bool enableInfiniteScroll,
double? height}) {
return CarouselSliderWidget<OnboardingBannerModel>(
height: height ?? MediaQuery.sizeOf(context).height,
items: [
OnboardingBannerModel(
image: Assets.image.onboarding.banner1,
desc:
'Proxibuy Geofencing marketing\n"Join the app to discover exclusive discounts and special offers in specific areas around you for a smarter shopping experience!"'),
OnboardingBannerModel(
image: Assets.image.onboarding.banner2,
desc:
'Proxibuy Geofencing marketing\n"Join the app to discover exclusive discounts and special offers in specific areas around you for a smarter shopping experience!"'),
OnboardingBannerModel(
image: Assets.image.onboarding.banner3,
desc:
'Proxibuy Geofencing marketing\n"Join the app to discover exclusive discounts and special offers in specific areas around you for a smarter shopping experience!"'),
],
withNavs: withNavs,
autoPlay: autoPlay,
enableInfiniteScroll: enableInfiniteScroll,
onLastClick: () {
setState(() {
onBoarding = false;
});
},
onPageBuilder: (context, item) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(child: item.image.image()),
if (Responsive(context).isDesktop()) 64.h,
Text(item.desc)
],
);
},
);
}
}

View File

@ -0,0 +1,17 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:proxibuy/core/routes/app_router.dart';
class ExploreScreen extends StatelessWidget {
const ExploreScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: InkWell(
onTap: () => context.go(AppRouter.setting), child: Text('Explore')),
),
);
}
}

View File

@ -1,80 +1,223 @@
import 'package:bottom_navy_bar/bottom_navy_bar.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart'; import 'package:proxibuy/core/gen/assets.gen.dart';
import 'package:proxibuy/core/routes/app_router.dart';
import 'package:proxibuy/core/services/permisions_services.dart';
import 'package:proxibuy/core/utils/empty_space.dart'; import 'package:proxibuy/core/utils/empty_space.dart';
import 'package:proxibuy/data/models/screen_model.dart';
import 'package:proxibuy/presentation/providers/cubit/them_mode_cubit.dart'; import 'package:proxibuy/presentation/providers/cubit/them_mode_cubit.dart';
import 'package:proxibuy/presentation/ui/screens/home/explore_screen.dart';
import 'package:proxibuy/presentation/ui/screens/home/home_screen.dart';
import 'package:proxibuy/presentation/ui/screens/home/setting_screen.dart';
import 'package:proxibuy/presentation/ui/theme/responsive.dart';
import 'package:proxibuy/presentation/ui/theme/theme.dart';
class MyHomePage extends StatefulWidget { class HomePage extends StatefulWidget {
const MyHomePage({super.key}); const HomePage({super.key});
@override @override
State<MyHomePage> createState() => _MyHomePageState(); State<HomePage> createState() => _HomePageState();
} }
class _MyHomePageState extends State<MyHomePage> { class _HomePageState extends State<HomePage> {
static const platform = MethodChannel('custom_notification'); int selectedIndex = 0;
List<ScreenModel> screens = [
Future<void> showCustomNotification() async { ScreenModel(title: 'Home', icon: Assets.icon.outline.home),
try { ScreenModel(title: 'Explore', icon: Assets.icon.outline.map),
final notifPer = await requestNotificationPermission(); ScreenModel(title: 'Settings', icon: Assets.icon.outline.setting),
if (notifPer) { ];
await platform.invokeMethod('showCustomNotification', {
'title': 'Handbags & Wallets | White Elm',
'content': 'Leander, Texas (8.7 Km away)',
'imageUrl':
'https://s3-alpha-sig.figma.com/img/652b/471b/a8059768c434c7abf4cfdc3c879e9293?Expires=1740355200&Key-Pair-Id=APKAQ4GOSFWCW27IBOMQ&Signature=EVePXMyyhpSNgasV3uH1d5qMrPSkjn5KzpSxFoySpBs9uzU1aNHQoab9G9iOliFDRRSZ62RggnoomQ52og0UTPnMBUG6c-5GKT~f-C~sGnqppDoNf9UhhCBXpbE-BJYvkdXyuc7YmcdVyljgcWfChHANQL~nz5egKp-nyoL6mRfa6z1AH5IVaD-hMcA~cgcM2iRiJxddCciGyRNyr0cS8XWilO55sA1blB-q2Bp9sKZJCezw5Nhn2S8SLSVnDrNjBAJv3ph4ArsCxHH5DhCNmPiDA~AT3LOz~lQwVVJZhkIEAK6zS1TpvrsPPfNp7gAgr4MG3GM2CGl19AwrSoxDdw__',
});
}
} on PlatformException catch (e) {
print("Failed to show notification: '${e.message}'.");
}
}
@override
void initState() {
super.initState();
platform.setMethodCallHandler(_handleMethod);
}
Future<void> _handleMethod(MethodCall call) async {
print(' method ${call.method}');
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Responsive(context).builder(
appBar: AppBar( desktop: Scaffold(
title: Text("Proxibuy"), body: Column(
actions: [ children: [
IconButton( navBar(context),
icon: Icon(Icons.brightness_6), Expanded(child: body()),
onPressed: () {
context.read<ThemModeCubit>().changeTheme();
},
),
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () async {
await showCustomNotification();
},
child: Text('Show Notification')),
18.h,
ElevatedButton(
onPressed: () async {
context.go(AppRouter.details);
},
child: Text('Navigate to Details')),
], ],
), ),
), ),
mobile: Scaffold(
appBar: AppBar(
title: Text(
"Proxibuy",
style: Theme.of(context).textTheme.headlineSmall,
),
actions: [
IconButton(
icon: Assets.icon.outline.notificationBing
.svg(color: Theme.of(context).colorScheme.onSurface),
onPressed: () {},
),
12.w,
IconButton(
icon: Icon(Icons.brightness_6),
onPressed: () {
context.read<ThemModeCubit>().changeTheme();
},
),
],
),
body: body(),
bottomNavigationBar: bottomNavigationBar(context),
),
); );
} }
Padding navBar(BuildContext context) {
final defaultBorder = OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide:
BorderSide(color: Theme.of(context).colorScheme.surface, width: 2));
return Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Row(
children: [
SelectableText(
"Proxibuy",
style: Theme.of(context).textTheme.displaySmall,
),
32.w,
Row(
children: [
...List.generate(
screens.length,
(index) => Row(
children: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: selectedIndex == index
? themeColor(context)
?.primaryLightSurface
.withAlpha(90)
: null),
child: IconButton(
splashRadius: 12,
onPressed: () {
_onItemTapped(context, index);
},
icon: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
screens[index].icon.svg(
color: selectedIndex == index
? Theme.of(context).primaryColor
: Theme.of(context)
.colorScheme
.onSurface),
12.w,
Text(
screens[index].title,
style: Theme.of(context)
.textTheme
.labelLarge
?.copyWith(
color: selectedIndex == index
? Theme.of(context)
.primaryColor
: Theme.of(context)
.colorScheme
.onSurface),
)
],
)),
),
24.w
],
),
)
],
),
8.w,
Flexible(
child: Container(
constraints: BoxConstraints(maxWidth: 800),
child: TextField(
decoration: InputDecoration(
hintText: 'what are you looking for?',
suffixIcon: Padding(
padding: const EdgeInsets.all(8.0),
child: Assets.icon.outline.search.svg(
color: Theme.of(context).colorScheme.onSurface,
width: 16,
height: 16),
),
enabledBorder: defaultBorder,
border: defaultBorder),
),
)),
32.w,
],
),
),
Row(
children: [
IconButton(
icon: Assets.icon.outline.notificationBing
.svg(color: Theme.of(context).colorScheme.onSurface),
onPressed: () {},
),
12.w,
IconButton(
icon: Icon(Icons.brightness_6),
onPressed: () {
context.read<ThemModeCubit>().changeTheme();
},
),
],
)
],
),
);
}
Padding bottomNavigationBar(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: BottomNavyBar(
selectedIndex: selectedIndex,
// landscapeLayout: BottomNavigationBarLandscapeLayout.centered,
// showUnselectedLabels: false,
onItemSelected: (index) => _onItemTapped(context, index),
backgroundColor: Theme.of(context).colorScheme.surface,
itemCornerRadius: 12,
itemPadding: EdgeInsets.symmetric(horizontal: 16),
borderRadius: BorderRadius.circular(16),
mainAxisAlignment: MainAxisAlignment.spaceAround,
showElevation: true,
items: [
...List.generate(
screens.length,
(index) => BottomNavyBarItem(
icon: screens[index].icon.svg(
color: selectedIndex == index
? Theme.of(context).primaryColor
: Theme.of(context).colorScheme.onSurface),
activeColor: Theme.of(context).primaryColor,
inactiveColor: Theme.of(context).colorScheme.onSurface,
title: Text(screens[index].title)),
)
],
),
);
}
IndexedStack body() {
return IndexedStack(
sizing: StackFit.expand,
index: selectedIndex,
children: [HomeScreen(), ExploreScreen(), SettingScreen()],
);
}
void _onItemTapped(BuildContext context, int index) {
setState(() {
selectedIndex = index;
});
}
} }

View File

@ -0,0 +1,74 @@
import 'package:flutter/material.dart';
import 'package:proxibuy/core/gen/assets.gen.dart';
import 'package:proxibuy/presentation/ui/theme/responsive.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
final defaultBorder = OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide:
BorderSide(color: Theme.of(context).colorScheme.surface, width: 2));
return Responsive(context).builder(
mobile: Scaffold(
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
// ElevatedButton(
// onPressed: () async {
// final notifPer = await requestNotificationPermission();
// if (notifPer) {
// if (kIsWeb) {
// NotificationServiceWeb.showNotification();
// } else {
// await NotificationSrviceAndroid.showCustomNotification();
// }
// }
// },
// child: Text('Show Notification')),
Flexible(
child: Container(
constraints: BoxConstraints(maxWidth: 800),
child: TextField(
textInputAction: TextInputAction.search,
onSubmitted: (value) {
print(value);
},
decoration: InputDecoration(
hintText: 'what are you looking for?',
suffixIcon: Padding(
padding: const EdgeInsets.all(12.0),
child: Assets.icon.outline.search.svg(
color: Theme.of(context).colorScheme.onSurface,
width: 16,
height: 16),
),
enabledBorder: defaultBorder,
border: defaultBorder),
),
)),
],
),
),
),
desktop: Scaffold(
body: SizedBox(),
));
}
}

View File

@ -1,13 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class DetailsPage extends StatelessWidget { class SettingScreen extends StatelessWidget {
const DetailsPage({super.key}); const SettingScreen({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
body: Center( body: Center(
child: Text('Detail Page'), child: Text('Setting'),
), ),
); );
} }

View File

@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
class CustomColors extends ThemeExtension<CustomColors> {
final MaterialColor primarySwatch;
final MaterialColor secondrySwatch;
final Color primaryLightSurface;
CustomColors({
required this.primarySwatch,
required this.secondrySwatch,
required this.primaryLightSurface,
});
@override
CustomColors copyWith({
MaterialColor? primarySwatch,
MaterialColor? secondrySwatch,
Color? primaryLightSurface,
}) {
return CustomColors(
primarySwatch: primarySwatch ?? this.primarySwatch,
secondrySwatch: secondrySwatch ?? this.secondrySwatch,
primaryLightSurface: primaryLightSurface ?? this.primaryLightSurface,
);
}
@override
ThemeExtension<CustomColors> lerp(
ThemeExtension<CustomColors>? other, double t) {
if (other is! CustomColors) return this;
return CustomColors(
primarySwatch: other.primarySwatch,
secondrySwatch: other.secondrySwatch,
primaryLightSurface:
Color.lerp(primaryLightSurface, other.primaryLightSurface, t)!,
);
}
}
extension AppColorsTheme on ThemeData {
CustomColors? get customColors => extension<CustomColors>();
}

View File

@ -0,0 +1,48 @@
import 'package:flutter/material.dart';
class Responsive {
final BuildContext context;
Responsive(this.context);
Widget builder(
{required final Widget mobile,
required final Widget desktop,
final Widget? tablet,
final bool tabletAndMobileSame = false}) {
if (isMobile() || (isTablet() && tabletAndMobileSame)) {
return mobile;
} else if (isTablet() && tablet != null) {
return tablet;
} else {
return desktop;
}
}
Widget maxWidthInDesktop(
{final double maxWidth = 600,
required final Widget Function(
BuildContext contxet,
double mw,
) child}) {
if (isDesktop() || isTablet()) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ConstrainedBox(
constraints: BoxConstraints(maxWidth: maxWidth),
child: child(context, maxWidth),
),
],
);
} else {
return child(context, MediaQuery.sizeOf(context).width);
}
}
bool isMobile() => MediaQuery.sizeOf(context).width < 904;
bool isTablet() =>
MediaQuery.sizeOf(context).width < 1280 &&
MediaQuery.sizeOf(context).width >= 904;
bool isDesktop() => MediaQuery.sizeOf(context).width >= 1280;
}

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:proxibuy/presentation/ui/theme/custom_colors.dart';
import 'colors.dart'; import 'colors.dart';
final ThemeData appTheme = ThemeData( final ThemeData appTheme = ThemeData(
@ -6,9 +7,18 @@ final ThemeData appTheme = ThemeData(
primaryColor: primarySwatch[500], primaryColor: primarySwatch[500],
scaffoldBackgroundColor: lightBackground, scaffoldBackgroundColor: lightBackground,
extensions: <ThemeExtension<dynamic>>[
CustomColors(
primarySwatch: primarySwatch,
secondrySwatch: secondarySwatch,
primaryLightSurface: primarySwatch[200]!,
),
],
// App Bar Theme // App Bar Theme
appBarTheme: const AppBarTheme( appBarTheme: const AppBarTheme(
backgroundColor: primarySwatch, surfaceTintColor: lightBackground,
backgroundColor: lightBackground,
elevation: 2, elevation: 2,
titleTextStyle: TextStyle( titleTextStyle: TextStyle(
color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold), color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold),
@ -76,10 +86,18 @@ final ThemeData darkTheme = ThemeData(
primarySwatch: darkPrimarySwatch, primarySwatch: darkPrimarySwatch,
primaryColor: darkPrimarySwatch[500], primaryColor: darkPrimarySwatch[500],
scaffoldBackgroundColor: darkBackground, scaffoldBackgroundColor: darkBackground,
extensions: <ThemeExtension<dynamic>>[
CustomColors(
primarySwatch: darkPrimarySwatch,
secondrySwatch: darkSecondarySwatch,
primaryLightSurface: darkPrimarySwatch[900]!,
),
],
// App Bar Theme // App Bar Theme
appBarTheme: const AppBarTheme( appBarTheme: const AppBarTheme(
backgroundColor: darkPrimarySwatch, backgroundColor: darkBackground,
surfaceTintColor: darkBackground,
elevation: 2, elevation: 2,
titleTextStyle: TextStyle( titleTextStyle: TextStyle(
color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold), color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold),
@ -142,3 +160,7 @@ final ThemeData darkTheme = ThemeData(
onSurface: fontLight, onSurface: fontLight,
), ),
); );
CustomColors? themeColor(BuildContext context) {
return Theme.of(context).extension<CustomColors>();
}

View File

@ -0,0 +1,124 @@
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
class CarouselSliderWidget<T> extends StatefulWidget {
final List<T> items;
final Widget Function(
BuildContext context,
T item,
) onPageBuilder;
final bool withNavs;
final double height;
final bool autoPlay;
final bool enableInfiniteScroll;
final Function()? onLastClick;
const CarouselSliderWidget({
super.key,
required this.items,
required this.onPageBuilder,
this.withNavs = false,
this.onLastClick,
this.autoPlay = true,
this.enableInfiniteScroll = true,
this.height = 200,
});
@override
State<CarouselSliderWidget<T>> createState() =>
_CarouselSliderWidgetState<T>();
}
class _CarouselSliderWidgetState<T> extends State<CarouselSliderWidget<T>> {
int activeIndex = 0;
final CarouselSliderController _controller = CarouselSliderController();
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CarouselSlider.builder(
carouselController: _controller,
itemCount: widget.items.length,
itemBuilder: (context, index, realIndex) {
return widget.onPageBuilder(context, widget.items[index]);
},
options: CarouselOptions(
height: widget.height * 0.75,
autoPlay: widget.autoPlay,
enableInfiniteScroll: widget.enableInfiniteScroll,
enlargeCenterPage: true,
viewportFraction: 0.9,
onPageChanged: (index, reason) =>
setState(() => activeIndex = index),
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(child: Center(child: buildIndicator())),
if (widget.withNavs)
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Opacity(
opacity: activeIndex != 0 ? 1 : 0,
child: IconButton(
icon: const Icon(Icons.arrow_back_ios_new_rounded),
onPressed: () => _controller.previousPage(),
),
),
Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).primaryColor),
child: InkWell(
highlightColor: Theme.of(context).primaryColor,
onTap: () {
if (activeIndex == widget.items.length - 1) {
if (widget.onLastClick != null) {
widget.onLastClick!();
}
} else {
_controller.nextPage();
}
},
child: const Icon(Icons.arrow_forward_ios_rounded),
),
),
],
),
),
],
),
),
],
);
}
Widget buildImage(String urlImage, int index) => Container(
margin: const EdgeInsets.symmetric(horizontal: 12),
color: Colors.grey,
child: Image.network(
urlImage,
fit: BoxFit.cover,
),
);
Widget buildIndicator() => AnimatedSmoothIndicator(
activeIndex: activeIndex,
count: widget.items.length,
effect: ExpandingDotsEffect(
dotWidth: 10,
dotHeight: 10,
activeDotColor: Theme.of(context).primaryColor,
dotColor: Theme.of(context).colorScheme.onSurface,
),
);
}

View File

@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import 'package:intl_phone_field/intl_phone_field.dart';
class PhoneNumberInput extends StatelessWidget {
const PhoneNumberInput({super.key});
@override
Widget build(BuildContext context) {
final border = OutlineInputBorder(
borderSide:
BorderSide(color: Theme.of(context).colorScheme.onSurface, width: 2),
);
return IntlPhoneField(
decoration: InputDecoration(
labelText: 'Phone number',
counter: SizedBox(),
border: border,
enabledBorder: border),
initialCountryCode: 'US',
dropdownIconPosition: IconPosition.trailing,
flagsButtonMargin: EdgeInsets.symmetric(horizontal: 12),
onChanged: (phone) {
print(phone.completeNumber);
},
);
}
}

View File

@ -0,0 +1,62 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:proxibuy/core/routes/app_router.dart';
class CustomBottomNav extends StatelessWidget {
final Widget child;
const CustomBottomNav({super.key, required this.child});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
child,
Positioned(
bottom: 16,
left: 16,
right: 16,
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: BottomNavigationBar(
currentIndex: _getSelectedIndex(context),
landscapeLayout: BottomNavigationBarLandscapeLayout.centered,
onTap: (index) => _onItemTapped(context, index),
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home), label: "Home"),
BottomNavigationBarItem(
icon: Icon(Icons.explore), label: "Explore"),
BottomNavigationBarItem(
icon: Icon(Icons.settings), label: "Settings"),
],
),
),
)
],
),
);
}
int _getSelectedIndex(BuildContext context) {
final String location = GoRouterState.of(context).uri.toString();
if (location.startsWith(AppRouter.explore)) return 1;
if (location.startsWith(AppRouter.setting)) return 2;
return 0;
}
void _onItemTapped(BuildContext context, int index) {
switch (index) {
case 0:
context.go(AppRouter.initial);
break;
case 1:
context.go(AppRouter.explore);
break;
case 2:
context.go(AppRouter.setting);
break;
}
}
}

View File

@ -65,6 +65,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" version: "2.1.1"
bottom_navy_bar:
dependency: "direct main"
description:
name: bottom_navy_bar
sha256: ed54e0a18748106cc4d3648390bf94e88c0913db6bd3018dd30ab0810f0c26d6
url: "https://pub.dev"
source: hosted
version: "6.1.0"
build: build:
dependency: transitive dependency: transitive
description: description:
@ -129,6 +137,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.9.3" version: "8.9.3"
carousel_slider:
dependency: "direct main"
description:
name: carousel_slider
sha256: "7b006ec356205054af5beaef62e2221160ea36b90fb70a35e4deacd49d0349ae"
url: "https://pub.dev"
source: hosted
version: "5.0.0"
characters: characters:
dependency: transitive dependency: transitive
description: description:
@ -137,6 +153,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.0" version: "1.3.0"
charcode:
dependency: transitive
description:
name: charcode
sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a
url: "https://pub.dev"
source: hosted
version: "1.4.0"
checked_yaml: checked_yaml:
dependency: transitive dependency: transitive
description: description:
@ -249,6 +273,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.0" version: "2.1.0"
dropdown_textfield:
dependency: "direct main"
description:
name: dropdown_textfield
sha256: ef8a35c52c92a563773d3efead94e5a1c162d6fe6c53974d0986aab6249928a1
url: "https://pub.dev"
source: hosted
version: "1.2.0"
equatable:
dependency: transitive
description:
name: equatable
sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7"
url: "https://pub.dev"
source: hosted
version: "2.0.7"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -334,6 +374,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.4" version: "2.4.4"
flutter_svg:
dependency: "direct main"
description:
name: flutter_svg
sha256: c200fd79c918a40c5cd50ea0877fa13f81bdaf6f0a5d3dbcc2a13e3285d6aa1b
url: "https://pub.dev"
source: hosted
version: "2.0.17"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@ -432,6 +480,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.0" version: "2.4.0"
intl_phone_field:
dependency: "direct main"
description:
name: intl_phone_field
sha256: "73819d3dfcb68d2c85663606f6842597c3ddf6688ac777f051b17814fe767bbf"
url: "https://pub.dev"
source: hosted
version: "3.2.0"
io: io:
dependency: transitive dependency: transitive
description: description:
@ -672,6 +728,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.0.1" version: "6.0.1"
pretty_dio_logger:
dependency: "direct main"
description:
name: pretty_dio_logger
sha256: "36f2101299786d567869493e2f5731de61ce130faa14679473b26905a92b6407"
url: "https://pub.dev"
source: hosted
version: "1.4.0"
provider: provider:
dependency: transitive dependency: transitive
description: description:
@ -773,6 +837,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
smooth_page_indicator:
dependency: "direct main"
description:
name: smooth_page_indicator
sha256: "3b28b0c545fa67ed9e5997d9f9720d486f54c0c607e056a1094544e36934dff3"
url: "https://pub.dev"
source: hosted
version: "1.2.0+3"
source_span: source_span:
dependency: transitive dependency: transitive
description: description:
@ -853,6 +925,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.0" version: "1.4.0"
universal_html:
dependency: "direct main"
description:
name: universal_html
sha256: "56536254004e24d9d8cfdb7dbbf09b74cf8df96729f38a2f5c238163e3d58971"
url: "https://pub.dev"
source: hosted
version: "2.2.4"
universal_io: universal_io:
dependency: transitive dependency: transitive
description: description:
@ -869,6 +949,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.0" version: "0.3.0"
vector_graphics:
dependency: transitive
description:
name: vector_graphics
sha256: "44cc7104ff32563122a929e4620cf3efd584194eec6d1d913eb5ba593dbcf6de"
url: "https://pub.dev"
source: hosted
version: "1.1.18"
vector_graphics_codec: vector_graphics_codec:
dependency: transitive dependency: transitive
description: description:

View File

@ -42,6 +42,14 @@ dependencies:
url_strategy: ^0.3.0 url_strategy: ^0.3.0
flutter_native_splash: ^2.4.4 flutter_native_splash: ^2.4.4
flutter_launcher_icons: ^0.14.3 flutter_launcher_icons: ^0.14.3
carousel_slider: ^5.0.0
smooth_page_indicator: ^1.2.0+3
intl_phone_field: ^3.2.0
flutter_svg: ^2.0.17
bottom_navy_bar: ^6.1.0
pretty_dio_logger: ^1.4.0
universal_html: ^2.2.4
dropdown_textfield: ^1.2.0
flutter_launcher_icons: flutter_launcher_icons:
android: true android: true
@ -106,6 +114,10 @@ flutter:
# To add assets to your application, add an assets section, like this: # To add assets to your application, add an assets section, like this:
assets: assets:
- assets/logo/ - assets/logo/
- assets/image/
- assets/icon/
- assets/icon/outline/
- assets/image/onboarding/
# - images/a_dot_ham.jpeg # - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see # An image asset can refer to one or more resolution-specific "variants", see

View File

@ -1,4 +1,6 @@
<!DOCTYPE html><html><head> <!DOCTYPE html>
<html>
<head>
<!-- <!--
If you are serving your web app in a path other than the root, change the If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from. href value below to reflect the base path you are serving from.
@ -12,103 +14,129 @@
This is a placeholder for base href that will be replaced by the value of This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`. the `--base-href` argument provided to `flutter build`.
--> -->
<base href="$FLUTTER_BASE_HREF"> <base href="$FLUTTER_BASE_HREF" />
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta content="IE=Edge" http-equiv="X-UA-Compatible"> <meta content="IE=Edge" http-equiv="X-UA-Compatible" />
<meta name="description" content="A new Flutter project."> <meta name="description" content="A new Flutter project." />
<!-- iOS meta tags & icons --> <!-- iOS meta tags & icons -->
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="apple-mobile-web-app-title" content="proxibuy"> <meta name="apple-mobile-web-app-title" content="proxibuy" />
<link rel="apple-touch-icon" href="icons/Icon-192.png"> <link rel="apple-touch-icon" href="icons/Icon-192.png" />
<!-- Favicon --> <!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"> <link rel="icon" type="image/png" href="favicon.png" />
<title>proxibuy</title> <title>proxibuy</title>
<link rel="manifest" href="manifest.json"> <link rel="manifest" href="manifest.json" />
<style id="splash-screen-style"> <style id="splash-screen-style">
html { html {
height: 100% height: 100%;
} }
body {
margin: 0;
min-height: 100%;
background-color: #ffffff;
background-size: 100% 100%;
}
.center {
margin: 0;
position: absolute;
top: 50%;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.contain {
display:block;
width:100%; height:100%;
object-fit: contain;
}
.stretch {
display:block;
width:100%; height:100%;
}
.cover {
display:block;
width:100%; height:100%;
object-fit: cover;
}
.bottom {
position: absolute;
bottom: 0;
left: 50%;
-ms-transform: translate(-50%, 0);
transform: translate(-50%, 0);
}
.bottomLeft {
position: absolute;
bottom: 0;
left: 0;
}
.bottomRight {
position: absolute;
bottom: 0;
right: 0;
}
@media (prefers-color-scheme: dark) {
body { body {
background-color: #121212; margin: 0;
} min-height: 100%;
} background-color: #ffffff;
</style> background-size: 100% 100%;
<script id="splash-screen-script"> }
function removeSplashFromWeb() {
document.getElementById("splash")?.remove(); .center {
document.getElementById("splash-branding")?.remove(); margin: 0;
document.body.style.background = "transparent"; position: absolute;
} top: 50%;
</script> left: 50%;
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"> -ms-transform: translate(-50%, -50%);
</head> transform: translate(-50%, -50%);
}
.contain {
display: block;
width: 100%;
height: 100%;
object-fit: contain;
}
.stretch {
display: block;
width: 100%;
height: 100%;
}
.cover {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.bottom {
position: absolute;
bottom: 0;
left: 50%;
-ms-transform: translate(-50%, 0);
transform: translate(-50%, 0);
}
.bottomLeft {
position: absolute;
bottom: 0;
left: 0;
}
.bottomRight {
position: absolute;
bottom: 0;
right: 0;
}
@media (prefers-color-scheme: dark) {
body {
background-color: #121212;
}
}
</style>
<script id="splash-screen-script">
function removeSplashFromWeb() {
document.getElementById("splash")?.remove();
document.getElementById("splash-branding")?.remove();
document.body.style.background = "transparent";
}
</script>
<meta
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
name="viewport"
/>
</head>
<body style="background-color: #1c1c1c"> <body style="background-color: #1c1c1c">
<picture id="splash"> <picture id="splash">
<source srcset="splash/img/light-1x.png 1x, splash/img/light-2x.png 2x, splash/img/light-3x.png 3x, splash/img/light-4x.png 4x" media="(prefers-color-scheme: light)"> <source
<source srcset="splash/img/dark-1x.png 1x, splash/img/dark-2x.png 2x, splash/img/dark-3x.png 3x, splash/img/dark-4x.png 4x" media="(prefers-color-scheme: dark)"> srcset="
<img class="center" aria-hidden="true" src="splash/img/light-1x.png" alt=""> splash/img/light-1x.png 1x,
</picture> splash/img/light-2x.png 2x,
splash/img/light-3x.png 3x,
splash/img/light-4x.png 4x
"
media="(prefers-color-scheme: light)"
/>
<source
srcset="
splash/img/dark-1x.png 1x,
splash/img/dark-2x.png 2x,
splash/img/dark-3x.png 3x,
splash/img/dark-4x.png 4x
"
media="(prefers-color-scheme: dark)"
/>
<img
class="center"
aria-hidden="true"
src="splash/img/light-1x.png"
alt=""
/>
</picture>
<script src="flutter_bootstrap.js" async=""></script> <script src="flutter_bootstrap.js" async=""></script>
</body>
</html>
</body></html>