E-Commerce Example
User Profile Modules
Account management, addresses, and user settings.
User Profile Modules
The Profile section is a clean, multi-page module that isolates individual account settings files for exceptional organization and reusability.
Main Profile Component (lib/features/profile/profile.view.dart)
import 'package:flutter/material.dart';
import 'package:fluxy/fluxy.dart';
import '../../features/home/home.controller.dart';
class ProfileView extends StatelessWidget {
final HomeController controller;
const ProfileView({super.key, required this.controller});
@override
Widget build(BuildContext context) {
return Fx.scaffold(
backgroundColor: Colors.transparent,
appBar: Fx.appBar(
title: 'Profile',
backgroundColor: Colors.transparent,
foregroundColor: controller.text,
elevation: 0,
centerTitle: false,
),
body: Builder(
builder: (context) {
return Fx.col(
children: [
// Profile Header
Fx(
() => Fx.col(
alignItems: CrossAxisAlignment.center,
children: [
Fx.stack(
children: [
Fx.image(
controller.userAvatar.value,
width: 100,
height: 100,
fit: BoxFit.cover,
radius: 50,
),
Positioned(
bottom: 0,
right: 0,
child: Fx.box()
.w(32)
.h(32)
.bg
.black
.circle()
.border(color: Colors.white, width: 3)
.center()
.pointer()
.child(
const Icon(
Icons.edit,
color: Colors.white,
size: 16,
),
),
),
],
).tw('mb-4'),
Fx.text(
controller.userName.value,
).font.xl2().bold().color(controller.text),
Fx.text(controller.userEmail.value).font.md().muted(),
],
),
).tw('w-full pt-8 pb-6').my(24),
// Quick Stats
Fx(
() => Fx.row(
children: [
_buildStatCard(
'Orders',
'${controller.ordersCount.value}',
).expanded(),
Fx.gap(12),
_buildStatCard(
'Wishlist',
'${controller.wishlist.value.length}',
).expanded(),
Fx.gap(12),
_buildStatCard(
'Points',
'${controller.points.value}',
).expanded(),
],
).px(24).pb(32),
),
// Settings Groups
Fx.col(
alignItems: CrossAxisAlignment.start,
children: [
Fx.text(
'Account Settings'.toUpperCase(),
).font.xs().bold().color(Colors.grey.shade500).px(24).pb(8),
Fx.col(
children: [
_buildGroupTile(
Icons.person_outline,
'Personal Information',
onTap: () => Fluxy.to(
'/personal-info',
arguments: {'controller': controller},
),
),
_buildDivider(),
_buildGroupTile(
Icons.location_on_outlined,
'Shipping Addresses',
onTap: () => Fluxy.to('/shipping', arguments: {'controller': controller}),
),
_buildDivider(),
_buildGroupTile(
Icons.payment_outlined,
'Payment Methods',
onTap: () => Fluxy.to('/payment', arguments: {'controller': controller}),
),
],
)
.bg(controller.surface)
.rounded(20)
.shadowSmall()
.mx(24)
.mb(24),
Fx.text(
'Preferences'.toUpperCase(),
).font.xs().bold().color(Colors.grey.shade500).px(24).pb(8),
Fx(
() => Fx.col(
children: [
_buildGroupTile(
Icons.notifications_outlined,
'Notifications',
switchValue:
controller.notificationsEnabled.value,
onSwitchChanged: (v) =>
controller.notificationsEnabled.value = v,
),
_buildDivider(),
_buildGroupTile(
Icons.language_outlined,
'Language',
onTap: () {},
trailingText: controller.language.value,
),
_buildDivider(),
_buildGroupTile(
Icons.dark_mode_outlined,
'Dark Mode',
switchValue: controller.isDarkMode.value,
onSwitchChanged: (v) {
controller.toggleTheme(v);
},
),
],
),
)
.bg(controller.surface)
.rounded(20)
.shadowSmall()
.mx(24)
.mb(32),
// Logout Button (Mixed Tailwind & DSL)
Fx.box()
.tw('w-full py-4 rounded-xl shadow-md')
.bg(controller.surface)
.shadowSmall()
.pointer()
.center()
.child(
Fx.text(
'Log Out',
).tw('text-lg font-bold').color(Colors.redAccent),
)
.onTap(() {})
.tw('w-full mx-6 mb-10')
.mx(24)
.mb(32),
],
),
],
).scrollable();
},
),
);
}
Widget _buildStatCard(String title, String value) {
return Fx.col(
alignItems: CrossAxisAlignment.center,
children: [
Fx.text(value).tw('text-2xl font-bold').color(controller.text),
Fx.gap(4),
Fx.text(title).tw('text-sm').color(controller.textMuted),
],
).tw('p-4 rounded-xl shadow-md').bg(controller.surface);
}
Widget _buildGroupTile(
IconData icon,
String title, {
VoidCallback? onTap,
String? trailingText,
bool? switchValue,
ValueChanged<bool>? onSwitchChanged,
}) {
return Fx.row(
children: [
Fx.box()
.tw('p-2.5 rounded-xl')
.bg(
controller.isDarkMode.value
? Colors.grey.shade800
: Colors.grey.shade100,
)
.child(Icon(icon, color: controller.text, size: 20)),
Fx.gap(16),
Fx.text(
title,
).tw('text-lg font-medium').color(controller.text).expanded(),
if (trailingText != null)
Fx.text(
trailingText,
).tw('text-base pr-2').color(controller.textMuted),
if (switchValue != null)
Switch(
value: switchValue,
onChanged: onSwitchChanged,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
)
else
Icon(Icons.chevron_right, color: Colors.grey.shade400, size: 24),
],
).tw('px-4 py-3 pointer').onTap(() => onTap?.call());
}
Widget _buildDivider() {
return Fx.box()
.tw('w-full mx-4')
.h(1)
.bg(
controller.isDarkMode.value
? Colors.grey.shade800
: Colors.grey.shade100,
);
}
}Personal Information Edit (lib/features/profile/personal_info.view.dart)
import 'package:flutter/material.dart';
import 'package:fluxy/fluxy.dart';
import '../../features/home/home.controller.dart';
class PersonalInfoView extends StatelessWidget {
final HomeController controller;
const PersonalInfoView({super.key, required this.controller});
@override
Widget build(BuildContext context) {
return Fx(() {
final isDark = controller.isDarkMode.value;
return Fx.scaffold(
backgroundColor: isDark ? const Color(0xFF121212) : Colors.grey.shade50,
appBar: Fx.appBar(
title: 'Personal Info',
backgroundColor: Colors.transparent,
foregroundColor: controller.text,
elevation: 0,
centerTitle: false,
),
body: Fx.col(
alignItems: CrossAxisAlignment.start,
children: [
// Profile Avatar
SizedBox(
width: 104,
height: 104,
child: Stack(
clipBehavior: Clip.none,
alignment: Alignment.center,
children: [
Fx.image(
controller.userAvatar.value,
width: 104,
height: 104,
fit: BoxFit.cover,
radius: 52,
error: Icon(Icons.person, size: 50, color: controller.textMuted),
),
Positioned.fill(
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.blueAccent.withOpacity(0.3), width: 4),
),
),
),
Positioned(
bottom: -2,
right: -2,
child: Fx.box()
.p(8)
.bg(Colors.blueAccent)
.circle()
.border(color: isDark ? const Color(0xFF121212) : Colors.grey.shade50, width: 3)
.child(const Icon(Icons.camera_alt, size: 14, color: Colors.white))
.pointer()
),
]
)
).center().pb(32),
Fx.text('Name').font.md().bold().color(controller.text).pb(8),
Fx.input(
signal: controller.userName,
placeholder: 'Enter your name',
icon: Icons.person_outline,
).bg(isDark ? const Color(0xFF1E1E1E) : Colors.white)
.border(color: isDark ? Colors.grey.shade800 : Colors.grey.shade200).shadowSmall().rounded(16).mb(24),
Fx.text('Email').font.md().bold().color(controller.text).pb(8),
Fx.input(
signal: controller.userEmail,
placeholder: 'Enter your email',
icon: Icons.email_outlined,
).bg(isDark ? const Color(0xFF1E1E1E) : Colors.white)
.border(color: isDark ? Colors.grey.shade800 : Colors.grey.shade200).shadowSmall().rounded(16).mb(32),
Fx.box()
.wFull().py(18)
.bg(Colors.blueAccent)
.rounded(16)
.pointer()
.shadowSmall()
.center()
.child(Fx.text('Save Changes').font.lg().bold().color(Colors.white))
.onTap(() {
Fluxy.back();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Changes saved successfully!'), backgroundColor: Colors.green)
);
}),
],
).p(24).scrollable(),
);
});
}
}Payment Methods Setup (lib/features/profile/payment_method.view.dart)
import 'package:flutter/material.dart';
import 'package:fluxy/fluxy.dart';
import '../../features/home/home.controller.dart';
class PaymentMethodView extends StatelessWidget {
final HomeController controller;
const PaymentMethodView({super.key, required this.controller});
@override
Widget build(BuildContext context) {
return Fx(() => Fx.scaffold(
backgroundColor: controller.surface,
appBar: Fx.appBar(
title: 'Payment Methods',
backgroundColor: Colors.transparent,
foregroundColor: controller.text,
elevation: 0,
),
body: Fx.col(
alignItems: CrossAxisAlignment.start,
children: [
...controller.paymentCards.value.map((payment) {
final isSelected = controller.selectedPaymentId.value == payment.id;
return _buildCardInfo(payment, isSelected).mb(20);
}),
Fx.gap(16),
Fx.box()
.wFull().py(18)
.bg.transparent
.border(color: controller.text, width: 2)
.rounded(16)
.pointer()
.center()
.child(Fx.text('+ Add Payment Method').font.lg().bold().color(controller.text))
.onTap(() {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Add Card UI placeholder.')));
}),
],
).p(24).scrollable(),
));
}
Widget _buildCardInfo(PaymentCard card, bool isSelected) {
return Fx.col(
alignItems: CrossAxisAlignment.start,
children: [
Fx.row(
justify: MainAxisAlignment.spaceBetween,
children: [
Fx.row(
children: [
Icon(card.iconType == 'visa' ? Icons.credit_card : Icons.credit_card_outlined, color: controller.text),
Fx.gap(12),
Fx.text(card.bank).font.lg().bold().color(controller.text),
],
).expanded(),
Fx.box().bg(isSelected ? Colors.blueAccent : Colors.transparent).rounded(8).px(12).py(6).border(color: isSelected ? Colors.transparent : controller.textMuted).pointer().onTap((){
controller.selectedPaymentId.value = card.id;
Fluxy.back();
}).child(
Fx.text(isSelected ? 'Selected' : 'Select').font.xs().bold().color(isSelected ? Colors.white : controller.textMuted)
),
],
).pb(12),
Fx.text('**** **** **** ${card.last4}').font.xl().medium().color(controller.text).pb(4),
Fx.text('Expires 12/26').font.sm().color(controller.textMuted),
],
).p(20).bg(isSelected ? Colors.blueAccent.withOpacity(0.05) : controller.surface).rounded(16).border(color: isSelected ? Colors.blueAccent : (controller.isDarkMode.value ? Colors.white12 : Colors.grey.shade200), width: isSelected ? 2 : 1).wFull().pointer().onTap((){
controller.selectedPaymentId.value = card.id;
});
}
}Shipping Address Configuration (lib/features/profile/shipping_address.view.dart)
import 'package:flutter/material.dart';
import 'package:fluxy/fluxy.dart';
import '../../features/home/home.controller.dart';
class ShippingAddressView extends StatelessWidget {
final HomeController controller;
const ShippingAddressView({super.key, required this.controller});
@override
Widget build(BuildContext context) {
return Fx(
() => Fx.scaffold(
backgroundColor: controller.surface,
appBar: Fx.appBar(
title: 'Shipping Addresses',
backgroundColor: Colors.transparent,
foregroundColor: controller.text,
elevation: 0,
),
body: Fx.col(
alignItems: CrossAxisAlignment.start,
children: [
...controller.addresses.value.map((address) {
final isSelected =
controller.selectedAddressId.value == address.id;
return _buildAddressCard(address, isSelected).mb(20);
}),
Fx.gap(20),
Fx.box()
.wFull().py(18)
.bg.transparent
.border(color: controller.text, width: 2)
.rounded(16)
.pointer()
.center()
.child(
Fx.text(
'+ Add New Address',
).font.lg().bold().color(controller.text),
)
.onTap(() {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Add Address UI placeholder.'),
),
);
}),
],
).p(24).scrollable(),
),
);
}
Widget _buildAddressCard(Address address, bool isSelected) {
return Fx.col(
alignItems: CrossAxisAlignment.start,
children: [
Fx.row(
justify: MainAxisAlignment.spaceBetween,
children: [
Fx.text(
address.label,
).font.lg().bold().color(controller.text).expanded(),
if (address.isDefault)
Fx.box().bg(Colors.green.shade100).rounded(8).px(8).py(4).child(
Fx.text('Default').font.xs().bold().color(Colors.green.shade800)
),
],
).pb(8),
Fx.text(
address.fullAddress,
).font.md().color(controller.textMuted).pb(16),
Fx.row(
children: [
Fx.text('Select').font
.sm()
.bold()
.color(isSelected ? Colors.green : Colors.blueAccent)
.pointer()
.onTap(() {
controller.selectedAddressId.value = address.id;
Fluxy.back();
}),
Fx.gap(16),
Fx.text('Edit').font
.sm()
.bold()
.color(controller.textMuted)
.pointer()
.onTap(() {}),
Fx.gap(16),
Fx.text('Delete').font.sm().bold().color(Colors.redAccent).pointer().onTap((){}),
],
)
],
)
.p(16)
.bg(
isSelected ? Colors.blueAccent.withOpacity(0.05) : controller.surface,
)
.rounded(16)
.border(
color: isSelected
? Colors.blueAccent
: (controller.isDarkMode.value
? Colors.white12
: Colors.grey.shade200),
width: isSelected ? 2 : 1,
)
.wFull()
.pointer()
.onTap(() {
controller.selectedAddressId.value = address.id;
});
}
}📱 Visual Examples







