ui improvements
This commit is contained in:
@@ -11,6 +11,7 @@ class HomeScreenModel {
|
||||
List<Category> categories = [];
|
||||
List<BillProvider> providers = [];
|
||||
List<BillProvider> filterableProviders = [];
|
||||
List<Account> accounts = [];
|
||||
BillProvider? selectedProvider;
|
||||
dynamic selectedCategory;
|
||||
dynamic account = {};
|
||||
@@ -20,6 +21,26 @@ class HomeScreenModel {
|
||||
String? errorMessage;
|
||||
}
|
||||
|
||||
class Account {
|
||||
late String name;
|
||||
late String description;
|
||||
late String image;
|
||||
late String currency;
|
||||
late String balance;
|
||||
late String availableBalance;
|
||||
late String accountNumber;
|
||||
late String accountType;
|
||||
late String accountHolderName;
|
||||
|
||||
Account({
|
||||
required this.name,
|
||||
required this.description,
|
||||
required this.image,
|
||||
required this.currency,
|
||||
required this.balance,
|
||||
});
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class Category with _$Category {
|
||||
const factory Category({
|
||||
@@ -76,12 +97,17 @@ class HomeController extends ChangeNotifier {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> getAccounts() async {
|
||||
model.accounts = getFakeAccounts();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> getProviders() async {
|
||||
try {
|
||||
model.isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
List<dynamic> response = await http.get('/providers');
|
||||
List<dynamic> response = await http.get('/public/providers');
|
||||
model.providers = response.map((e) => BillProvider.fromJson(e)).toList();
|
||||
model.filterableProviders = model.providers;
|
||||
|
||||
@@ -116,7 +142,7 @@ class HomeController extends ChangeNotifier {
|
||||
|
||||
try {
|
||||
Map<String, dynamic> response = await http.get(
|
||||
'/transaction?sort=createdAt,desc&size=3&page=0&userId=$userId&type=REQUEST',
|
||||
'/public/transaction?sort=createdAt,desc&size=3&page=0&userId=$userId&type=REQUEST',
|
||||
);
|
||||
|
||||
PageableModel pageableModel = PageableModel.fromJson(response);
|
||||
@@ -146,7 +172,7 @@ class HomeController extends ChangeNotifier {
|
||||
|
||||
// Simulate fetching categories
|
||||
try {
|
||||
List<dynamic> response = await http.get('/categories');
|
||||
List<dynamic> response = await http.get('/public/categories');
|
||||
model.categories = response.map((e) => Category.fromJson(e)).toList();
|
||||
|
||||
if (model.categories.isNotEmpty) {
|
||||
@@ -246,4 +272,23 @@ class HomeController extends ChangeNotifier {
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
List<Account> getFakeAccounts() {
|
||||
return [
|
||||
Account(
|
||||
name: 'Vusumuzi Khoza',
|
||||
description: 'USD Wallet',
|
||||
image: 'united-states.png',
|
||||
currency: 'USD',
|
||||
balance: '4.02',
|
||||
),
|
||||
Account(
|
||||
name: 'Vusumuzi Khoza',
|
||||
description: 'ZWG Wallet',
|
||||
image: 'zimbabwe.png',
|
||||
currency: 'ZWL',
|
||||
balance: '36.00',
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
|
||||
Future<void> setupDataAndTransitions() async {
|
||||
homeController = HomeController(context);
|
||||
await homeController.getAccounts();
|
||||
await homeController.getCategories();
|
||||
await homeController.getProviders();
|
||||
|
||||
@@ -108,20 +109,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
}
|
||||
|
||||
getBoxDecoration(String? clientId) {
|
||||
// BillProvider? provider = homeController.model.selectedProvider;
|
||||
//
|
||||
// if (provider != null && clientId != provider.clientId) {
|
||||
// return BoxDecoration(
|
||||
// border: Border.all(color: Colors.black87.withAlpha(20)),
|
||||
// borderRadius: BorderRadius.circular(12),
|
||||
// );
|
||||
// }
|
||||
|
||||
return BoxDecoration(
|
||||
// border: Border.all(
|
||||
// color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.7),
|
||||
// width: 3,
|
||||
// ),
|
||||
border: Border.all(color: Colors.black87.withAlpha(20)),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
gradient: LinearGradient(
|
||||
@@ -161,12 +149,45 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
surfaceTintColor: Colors.transparent,
|
||||
floating: true,
|
||||
title: Image(image: AssetImage('assets/peak.png'), width: 85),
|
||||
actions: [
|
||||
// IconButton(
|
||||
// icon: const Icon(Icons.notifications_outlined),
|
||||
// onPressed: () {},
|
||||
// ),
|
||||
],
|
||||
leading: IconButton(
|
||||
icon: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.black87.withAlpha(20)),
|
||||
shape: BoxShape.circle,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Theme.of(
|
||||
context,
|
||||
).colorScheme.primary.withValues(alpha: 0.1),
|
||||
Theme.of(
|
||||
context,
|
||||
).colorScheme.tertiary.withValues(alpha: 0.05),
|
||||
],
|
||||
stops: const [0.3, 0.7],
|
||||
begin: Alignment.topRight,
|
||||
end: Alignment.bottomLeft,
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'VK',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressed: () {},
|
||||
),
|
||||
// actions: [
|
||||
// IconButton(
|
||||
// icon: const Icon(Icons.notifications_outlined),
|
||||
// onPressed: () {},
|
||||
// ),
|
||||
// ],
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
@@ -177,29 +198,8 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (_isLoggedIn)
|
||||
AlignTransition(
|
||||
alignment: _alignAnimation,
|
||||
child: Skeletonizer(
|
||||
enabled: _loading,
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
"Vusumuzi Khoza",
|
||||
style: TextStyle(fontSize: 15),
|
||||
),
|
||||
Text(
|
||||
"USD 4.02",
|
||||
style: TextStyle(
|
||||
fontSize: 30,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!_isLoggedIn) _buildLogin(homeController),
|
||||
if (_isLoggedIn) _buildAccounts(homeController),
|
||||
_buildFilterChips(homeController),
|
||||
SizedBox(height: 10),
|
||||
Column(
|
||||
@@ -209,7 +209,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const SizedBox(height: 10),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
@@ -273,6 +273,174 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLogin(HomeController controller) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'Register or Login for more',
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
_showFeaturesPopup(context);
|
||||
},
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 5, vertical: 2),
|
||||
child: Text(
|
||||
'features',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: Theme.of(context).colorScheme.primary,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
context.push('/onboarding/landing');
|
||||
},
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.primary.withValues(alpha: 0.15),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Text('Get Started', style: TextStyle(fontSize: 12)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Divider(
|
||||
color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.2),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAccounts(HomeController controller) {
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
...controller.model.accounts.map(
|
||||
(e) => Row(
|
||||
children: [
|
||||
Skeletonizer(
|
||||
enabled: controller.model.isLoading,
|
||||
child: SlideTransition(
|
||||
position: _alignListAnimations[0],
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
onTap: () {
|
||||
// context.push("/wallet");
|
||||
},
|
||||
child: Container(
|
||||
width: 150,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 12,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Colors.black87.withAlpha(20),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Theme.of(
|
||||
context,
|
||||
).colorScheme.primary.withValues(alpha: 0.1),
|
||||
Theme.of(
|
||||
context,
|
||||
).colorScheme.tertiary.withValues(alpha: 0.05),
|
||||
],
|
||||
stops: const [0.3, 0.7],
|
||||
begin: Alignment.topRight,
|
||||
end: Alignment.bottomLeft,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Image.asset('assets/${e.image}', width: 20),
|
||||
SizedBox(height: 5),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
e.currency,
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w100,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
Text(
|
||||
e.balance,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 22,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
// context.push("/withdraw");
|
||||
},
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 6,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.primary
|
||||
.withValues(alpha: 0.15),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Text(
|
||||
'Add Money',
|
||||
style: TextStyle(fontSize: 12),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTransactionItem(Map<String, dynamic> e, BuildContext context) {
|
||||
return SlideTransition(
|
||||
position: _alignListAnimations[0],
|
||||
@@ -463,4 +631,197 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showFeaturesPopup(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.transparent,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.rectangle,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
blurRadius: 20,
|
||||
offset: const Offset(0, 10),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Header with icon and title
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.primary.withValues(alpha: 0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Image(image: AssetImage("assets/peak.png"), width: 45),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Unlock Additional Features',
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Register or login to access these great features',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.onSurface.withOpacity(0.7),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Features list
|
||||
_buildFeatureItem(
|
||||
Icons.account_balance_wallet,
|
||||
'Dual Currency Wallets',
|
||||
'USD and ZWG wallets for prepayments that can be utilized later',
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildFeatureItem(
|
||||
Icons.history,
|
||||
'Transaction History',
|
||||
'All transactions are saved for reference from any device',
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildFeatureItem(
|
||||
Icons.people,
|
||||
'Recipient Management',
|
||||
'Save and manage recipient details for future use',
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildFeatureItem(
|
||||
Icons.schedule,
|
||||
'Smart Payments',
|
||||
'Schedule payments and split bills with friends',
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Action buttons
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
'Maybe Later',
|
||||
style: TextStyle(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.onSurface.withOpacity(0.6),
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
context.push('/onboarding/landing');
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.primary,
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
'Get Started',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFeatureItem(IconData icon, String title, String description) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Icon(
|
||||
icon,
|
||||
size: 20,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
description,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.onSurface.withOpacity(0.7),
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user