profile added

This commit is contained in:
moon 2026-04-23 11:22:41 +06:30
parent 4e8e6af86e
commit ae210a5990
20 changed files with 309 additions and 173 deletions

View File

@ -1,7 +1,7 @@
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
class LocalStorageService { class LocalStorageService {
Future<void> setBool(String key, bool value) async{ Future<void> setBool(String key, bool value) async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
await prefs.setBool(key, value); await prefs.setBool(key, value);
} }
@ -10,4 +10,14 @@ class LocalStorageService {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
return prefs.getBool(key); return prefs.getBool(key);
} }
Future<void> setString(String key, String value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(key, value);
}
Future<String?> getString(String key) async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString(key);
}
} }

View File

@ -3,6 +3,6 @@ enum AnalysisChartMetric { scans, pointsUsed }
extension AnalysisChartMetricX on AnalysisChartMetric { extension AnalysisChartMetricX on AnalysisChartMetric {
String get label => switch (this) { String get label => switch (this) {
AnalysisChartMetric.scans => 'Member scans', AnalysisChartMetric.scans => 'Member scans',
AnalysisChartMetric.pointsUsed => 'Coins redeemed', AnalysisChartMetric.pointsUsed => 'Points redeemed',
}; };
} }

View File

@ -1,16 +0,0 @@
import 'package:cb_prestige_qr/core/storage/local_storage_service.dart';
class FingerprintRepository {
final LocalStorageService storage;
FingerprintRepository(this.storage);
static const key = "fingerprint_enabled";
Future<bool> load() async {
return await storage.getBool(key) ?? false;
}
Future<void> save(bool value) async {
await storage.setBool(key, value);
}
}

View File

@ -0,0 +1,63 @@
import 'package:cb_prestige_qr/core/storage/local_storage_service.dart';
import 'package:cb_prestige_qr/features/auth/domain/user_profile.dart';
class UserProfileRepository {
UserProfileRepository(this._storage);
final LocalStorageService _storage;
static const _usernameKey = 'logged_in_username';
static const _displayNameKey = 'logged_in_display_name';
static const _roleKey = 'logged_in_role';
static const _branchKey = 'logged_in_branch';
Future<UserProfile> load() async {
final username = await _storage.getString(_usernameKey) ?? 'cashier';
final displayName =
await _storage.getString(_displayNameKey) ??
_displayNameFromUsername(username);
final roleLabel = await _storage.getString(_roleKey) ?? 'Cashier';
final branchLabel =
await _storage.getString(_branchKey) ?? 'Prestige Counter';
return UserProfile(
username: username,
displayName: displayName,
roleLabel: roleLabel,
branchLabel: branchLabel,
);
}
Future<void> saveFromUsername(String username) async {
final normalizedUsername = username.trim();
final safeUsername = normalizedUsername.isEmpty
? 'cashier'
: normalizedUsername;
await _storage.setString(_usernameKey, safeUsername);
await _storage.setString(
_displayNameKey,
_displayNameFromUsername(safeUsername),
);
await _storage.setString(_roleKey, 'Cashier');
await _storage.setString(_branchKey, 'Prestige Counter');
}
static String _displayNameFromUsername(String username) {
final words = username
.replaceAll(RegExp(r'[._-]+'), ' ')
.trim()
.split(RegExp(r'\s+'))
.where((word) => word.isNotEmpty)
.toList(growable: false);
if (words.isEmpty) return 'Cashier';
return words.map(_capitalize).join(' ');
}
static String _capitalize(String value) {
if (value.isEmpty) return value;
if (value.length == 1) return value.toUpperCase();
return '${value.substring(0, 1).toUpperCase()}${value.substring(1)}';
}
}

View File

@ -1,9 +0,0 @@
class FingerprintState {
final bool enabled;
FingerprintState(this.enabled);
FingerprintState copyWith({bool? enabled}){
return FingerprintState(enabled ?? this.enabled);
}
}

View File

@ -0,0 +1,25 @@
class UserProfile {
const UserProfile({
required this.username,
required this.displayName,
required this.roleLabel,
required this.branchLabel,
});
final String username;
final String displayName;
final String roleLabel;
final String branchLabel;
String get initials {
final parts = displayName
.trim()
.split(RegExp(r'\s+'))
.where((part) => part.isNotEmpty)
.toList(growable: false);
if (parts.isEmpty) return 'U';
if (parts.length == 1) return parts.first.substring(0, 1).toUpperCase();
return '${parts.first.substring(0, 1)}${parts.last.substring(0, 1)}'
.toUpperCase();
}
}

View File

@ -1,6 +1,7 @@
import 'package:cb_prestige_qr/core/storage/local_storage_service.dart';
import 'package:cb_prestige_qr/core/utils/MainShell.dart'; import 'package:cb_prestige_qr/core/utils/MainShell.dart';
import 'package:cb_prestige_qr/features/auth/data/user_profile_repository.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:local_auth/local_auth.dart';
class LoginPage extends StatefulWidget { class LoginPage extends StatefulWidget {
const LoginPage({super.key}); const LoginPage({super.key});
@ -9,34 +10,17 @@ class LoginPage extends StatefulWidget {
State<LoginPage> createState() => _LoginPageState(); State<LoginPage> createState() => _LoginPageState();
} }
enum _SupportState { unknown, supported, unsupported }
class _LoginPageState extends State<LoginPage> { class _LoginPageState extends State<LoginPage> {
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
final _usernameController = TextEditingController(); final _usernameController = TextEditingController();
final _passwordController = TextEditingController(); final _passwordController = TextEditingController();
final LocalAuthentication auth = LocalAuthentication(); final UserProfileRepository _userProfileRepository = UserProfileRepository(
bool? _canCheckBiometric; LocalStorageService(),
List<BiometricType>? _availableBiometrics; );
String _authorized = "Not Authorized";
bool _isAuthenticating = false;
bool _isSigningIn = false; bool _isSigningIn = false;
_SupportState _supportState = _SupportState.unknown;
var _obscurePassword = true; var _obscurePassword = true;
@override
void initState(){
super.initState();
auth.isDeviceSupported().then(
(bool isSupported) => setState(
() => _supportState = isSupported
? _SupportState.supported
: _SupportState.unsupported,
),
);
}
@override @override
void dispose() { void dispose() {
_usernameController.dispose(); _usernameController.dispose();
@ -56,9 +40,15 @@ class _LoginPageState extends State<LoginPage> {
if (!mounted) return; if (!mounted) return;
Navigator.of(context).pushReplacement( await _userProfileRepository.saveFromUsername(
MaterialPageRoute(builder: (_) => const MainShell()), _usernameController.text.trim(),
); );
if (!mounted) return;
Navigator.of(
context,
).pushReplacement(MaterialPageRoute(builder: (_) => const MainShell()));
} }
@override @override
@ -72,11 +62,7 @@ class _LoginPageState extends State<LoginPage> {
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.topCenter, begin: Alignment.topCenter,
end: Alignment.bottomCenter, end: Alignment.bottomCenter,
colors: [ colors: [Color(0xff17181c), Color(0xff25262b), Color(0xff2c2d33)],
Color(0xff17181c),
Color(0xff25262b),
Color(0xff2c2d33),
],
), ),
), ),
child: SafeArea( child: SafeArea(
@ -88,12 +74,14 @@ class _LoginPageState extends State<LoginPage> {
child: Container( child: Container(
padding: const EdgeInsets.all(24), padding: const EdgeInsets.all(24),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xff1d1e23).withOpacity(0.92), color: const Color(0xff1d1e23).withValues(alpha: 0.92),
borderRadius: BorderRadius.circular(28), borderRadius: BorderRadius.circular(28),
border: Border.all(color: Colors.white.withOpacity(0.08)), border: Border.all(
color: Colors.white.withValues(alpha: 0.08),
),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.28), color: Colors.black.withValues(alpha: 0.28),
blurRadius: 28, blurRadius: 28,
offset: const Offset(0, 18), offset: const Offset(0, 18),
), ),
@ -111,7 +99,9 @@ class _LoginPageState extends State<LoginPage> {
height: 56, height: 56,
width: 56, width: 56,
decoration: BoxDecoration( decoration: BoxDecoration(
color: colorScheme.primary.withOpacity(0.16), color: colorScheme.primary.withValues(
alpha: 0.16,
),
borderRadius: BorderRadius.circular(18), borderRadius: BorderRadius.circular(18),
), ),
child: const Icon( child: const Icon(
@ -151,10 +141,10 @@ class _LoginPageState extends State<LoginPage> {
width: double.infinity, width: double.infinity,
padding: const EdgeInsets.all(18), padding: const EdgeInsets.all(18),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white.withOpacity(0.04), color: Colors.white.withValues(alpha: 0.04),
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
border: Border.all( border: Border.all(
color: Colors.white.withOpacity(0.06), color: Colors.white.withValues(alpha: 0.06),
), ),
), ),
child: Row( child: Row(

View File

@ -1,33 +0,0 @@
import 'package:cb_prestige_qr/core/storage/local_storage_service.dart';
import 'package:cb_prestige_qr/features/auth/data/fingerprint_repository.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hooks_riverpod/legacy.dart';
final storageProvider = Provider((ref) => LocalStorageService());
final fingerprintRepoProvider = Provider((ref) {
return FingerprintRepository(ref.read(storageProvider));
});
final fingerprintProvider =
StateNotifierProvider<FingerprintNotifier, bool>((ref) {
return FingerprintNotifier(ref.read(fingerprintRepoProvider));
});
class FingerprintNotifier extends StateNotifier<bool> {
final FingerprintRepository repo;
FingerprintNotifier(this.repo) : super(false) {
_init();
}
Future<void> _init() async {
state = await repo.load();
}
Future<void> toggle(bool value) async {
state = value;
await repo.save(value);
}
}

View File

@ -1,41 +1,41 @@
import 'package:cb_prestige_qr/core/storage/local_storage_service.dart';
import 'package:cb_prestige_qr/features/auth/data/user_profile_repository.dart';
import '../../domain/entities/settings_content.dart'; import '../../domain/entities/settings_content.dart';
abstract class SettingsLocalDataSource { abstract class SettingsLocalDataSource {
SettingsContent getSettings(); Future<SettingsContent> getSettings();
void setNotificationsEnabled(bool value); Future<void> setNotificationsEnabled(bool value);
void setHapticsEnabled(bool value); Future<void> setHapticsEnabled(bool value);
void setFingerEnabled(bool value);
} }
class SettingsLocalDataSourceImpl implements SettingsLocalDataSource { class SettingsLocalDataSourceImpl implements SettingsLocalDataSource {
SettingsLocalDataSourceImpl(); SettingsLocalDataSourceImpl(this._storage);
final LocalStorageService _storage;
bool _notificationsEnabled = true; bool _notificationsEnabled = true;
bool _hapticsEnabled = true; bool _hapticsEnabled = true;
bool _fingerEnabled = true;
@override @override
SettingsContent getSettings() { Future<SettingsContent> getSettings() async {
final userProfile = await UserProfileRepository(_storage).load();
return SettingsContent( return SettingsContent(
notificationsEnabled: _notificationsEnabled, notificationsEnabled: _notificationsEnabled,
hapticsEnabled: _hapticsEnabled, hapticsEnabled: _hapticsEnabled,
fingerEnabled: _fingerEnabled,
appVersionLabel: 'v1.0.0', appVersionLabel: 'v1.0.0',
userProfile: userProfile,
); );
} }
@override @override
void setNotificationsEnabled(bool value) { Future<void> setNotificationsEnabled(bool value) async {
_notificationsEnabled = value; _notificationsEnabled = value;
} }
@override @override
void setHapticsEnabled(bool value) { Future<void> setHapticsEnabled(bool value) async {
_hapticsEnabled = value; _hapticsEnabled = value;
} }
@override
void setFingerEnabled(bool value) {
_fingerEnabled = value;
}
} }

View File

@ -8,20 +8,15 @@ class SettingsRepositoryImpl implements SettingsRepository {
final SettingsLocalDataSource _localDataSource; final SettingsLocalDataSource _localDataSource;
@override @override
Future<SettingsContent> getSettings() async => _localDataSource.getSettings(); Future<SettingsContent> getSettings() => _localDataSource.getSettings();
@override @override
Future<void> setNotificationsEnabled(bool value) async { Future<void> setNotificationsEnabled(bool value) async {
_localDataSource.setNotificationsEnabled(value); await _localDataSource.setNotificationsEnabled(value);
} }
@override @override
Future<void> setHapticsEnabled(bool value) async { Future<void> setHapticsEnabled(bool value) async {
_localDataSource.setHapticsEnabled(value); await _localDataSource.setHapticsEnabled(value);
}
@override
Future<void> setFingerEnabled(bool value) async {
_localDataSource.setFingerEnabled(value);
} }
} }

View File

@ -1,13 +1,15 @@
import 'package:cb_prestige_qr/features/auth/domain/user_profile.dart';
class SettingsContent { class SettingsContent {
const SettingsContent({ const SettingsContent({
required this.notificationsEnabled, required this.notificationsEnabled,
required this.hapticsEnabled, required this.hapticsEnabled,
required this.fingerEnabled,
required this.appVersionLabel, required this.appVersionLabel,
required this.userProfile,
}); });
final bool notificationsEnabled; final bool notificationsEnabled;
final bool hapticsEnabled; final bool hapticsEnabled;
final bool fingerEnabled;
final String appVersionLabel; final String appVersionLabel;
final UserProfile userProfile;
} }

View File

@ -4,5 +4,4 @@ abstract class SettingsRepository {
Future<SettingsContent> getSettings(); Future<SettingsContent> getSettings();
Future<void> setNotificationsEnabled(bool value); Future<void> setNotificationsEnabled(bool value);
Future<void> setHapticsEnabled(bool value); Future<void> setHapticsEnabled(bool value);
Future<void> setFingerEnabled(bool value);
} }

View File

@ -1,9 +0,0 @@
import '../repositories/settings_repository.dart';
class SetFingerEnabled {
const SetFingerEnabled(this._repository);
final SettingsRepository _repository;
Future<void> call(bool value) => _repository.setFingerEnabled(value);
}

View File

@ -1,30 +1,32 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import '../../../auth/domain/user_profile.dart';
@immutable @immutable
class SettingsUiState { class SettingsUiState {
const SettingsUiState({ const SettingsUiState({
required this.notificationsEnabled, required this.notificationsEnabled,
required this.hapticsEnabled, required this.hapticsEnabled,
required this.fingerEnabled,
required this.appVersionLabel, required this.appVersionLabel,
required this.userProfile,
}); });
final bool notificationsEnabled; final bool notificationsEnabled;
final bool hapticsEnabled; final bool hapticsEnabled;
final bool fingerEnabled;
final String appVersionLabel; final String appVersionLabel;
final UserProfile userProfile;
SettingsUiState copyWith({ SettingsUiState copyWith({
bool? notificationsEnabled, bool? notificationsEnabled,
bool? hapticsEnabled, bool? hapticsEnabled,
bool? fingerEnabled,
String? appVersionLabel, String? appVersionLabel,
UserProfile? userProfile,
}) { }) {
return SettingsUiState( return SettingsUiState(
notificationsEnabled: notificationsEnabled ?? this.notificationsEnabled, notificationsEnabled: notificationsEnabled ?? this.notificationsEnabled,
hapticsEnabled: hapticsEnabled ?? this.hapticsEnabled, hapticsEnabled: hapticsEnabled ?? this.hapticsEnabled,
fingerEnabled: fingerEnabled ?? this.fingerEnabled,
appVersionLabel: appVersionLabel ?? this.appVersionLabel, appVersionLabel: appVersionLabel ?? this.appVersionLabel,
userProfile: userProfile ?? this.userProfile,
); );
} }
} }

View File

@ -1,17 +1,21 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../../../../core/storage/local_storage_service.dart';
import '../../data/data_sources/settings_local_data_source.dart'; import '../../data/data_sources/settings_local_data_source.dart';
import '../../data/repositories/settings_repository_impl.dart'; import '../../data/repositories/settings_repository_impl.dart';
import '../../domain/entities/settings_content.dart'; import '../../domain/entities/settings_content.dart';
import '../../domain/repositories/settings_repository.dart'; import '../../domain/repositories/settings_repository.dart';
import '../../domain/use_cases/get_settings.dart'; import '../../domain/use_cases/get_settings.dart';
import '../../domain/use_cases/set_finger_enabled.dart';
import '../../domain/use_cases/set_haptics_enabled.dart'; import '../../domain/use_cases/set_haptics_enabled.dart';
import '../../domain/use_cases/set_notifications_enabled.dart'; import '../../domain/use_cases/set_notifications_enabled.dart';
import 'settings_ui_state.dart'; import 'settings_ui_state.dart';
final _localStorageServiceProvider = Provider<LocalStorageService>(
(ref) => LocalStorageService(),
);
final _settingsLocalDataSourceProvider = Provider<SettingsLocalDataSource>( final _settingsLocalDataSourceProvider = Provider<SettingsLocalDataSource>(
(ref) => SettingsLocalDataSourceImpl(), (ref) => SettingsLocalDataSourceImpl(ref.watch(_localStorageServiceProvider)),
); );
final _settingsRepositoryProvider = Provider<SettingsRepository>( final _settingsRepositoryProvider = Provider<SettingsRepository>(
@ -30,10 +34,6 @@ final _setHapticsEnabledProvider = Provider<SetHapticsEnabled>(
(ref) => SetHapticsEnabled(ref.watch(_settingsRepositoryProvider)), (ref) => SetHapticsEnabled(ref.watch(_settingsRepositoryProvider)),
); );
final _setFingerEnabledProvider = Provider<SetFingerEnabled>(
(ref) => SetFingerEnabled(ref.watch(_settingsRepositoryProvider)),
);
final settingsViewModelProvider = final settingsViewModelProvider =
AsyncNotifierProvider<SettingsViewModel, SettingsUiState>( AsyncNotifierProvider<SettingsViewModel, SettingsUiState>(
SettingsViewModel.new, SettingsViewModel.new,
@ -50,8 +50,8 @@ class SettingsViewModel extends AsyncNotifier<SettingsUiState> {
return SettingsUiState( return SettingsUiState(
notificationsEnabled: content.notificationsEnabled, notificationsEnabled: content.notificationsEnabled,
hapticsEnabled: content.hapticsEnabled, hapticsEnabled: content.hapticsEnabled,
fingerEnabled: content.fingerEnabled,
appVersionLabel: content.appVersionLabel, appVersionLabel: content.appVersionLabel,
userProfile: content.userProfile,
); );
} }
@ -64,9 +64,4 @@ class SettingsViewModel extends AsyncNotifier<SettingsUiState> {
await ref.watch(_setHapticsEnabledProvider)(value); await ref.watch(_setHapticsEnabledProvider)(value);
state = state.whenData((s) => s.copyWith(hapticsEnabled: value)); state = state.whenData((s) => s.copyWith(hapticsEnabled: value));
} }
Future<void> toggleFinger(bool value) async {
await ref.watch(_setFingerEnabledProvider)(value);
state = state.whenData((s) => s.copyWith(fingerEnabled: value));
}
} }

View File

@ -3,6 +3,7 @@ import 'package:cb_prestige_qr/features/auth/presentation/pages/login_page.dart'
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../../../auth/domain/user_profile.dart';
import '../manager/settings_ui_state.dart'; import '../manager/settings_ui_state.dart';
import '../manager/settings_view_model.dart'; import '../manager/settings_view_model.dart';
@ -29,7 +30,6 @@ class SettingsPage extends ConsumerWidget {
state: state, state: state,
onNotificationsChanged: viewModel.toggleNotifications, onNotificationsChanged: viewModel.toggleNotifications,
onHapticsChanged: viewModel.toggleHaptics, onHapticsChanged: viewModel.toggleHaptics,
onFingerChanged: viewModel.toggleFinger,
onLogout: () { onLogout: () {
ref.read(navIndexNotifierProvider.notifier).setIndex(0); ref.read(navIndexNotifierProvider.notifier).setIndex(0);
Navigator.of(context).pushAndRemoveUntil( Navigator.of(context).pushAndRemoveUntil(
@ -55,14 +55,12 @@ class _SettingsBody extends StatelessWidget {
required this.state, required this.state,
required this.onNotificationsChanged, required this.onNotificationsChanged,
required this.onHapticsChanged, required this.onHapticsChanged,
required this.onFingerChanged,
required this.onLogout, required this.onLogout,
}); });
final SettingsUiState state; final SettingsUiState state;
final ValueChanged<bool> onNotificationsChanged; final ValueChanged<bool> onNotificationsChanged;
final ValueChanged<bool> onHapticsChanged; final ValueChanged<bool> onHapticsChanged;
final ValueChanged<bool> onFingerChanged;
final VoidCallback onLogout; final VoidCallback onLogout;
@override @override
@ -70,6 +68,8 @@ class _SettingsBody extends StatelessWidget {
return ListView( return ListView(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 24), padding: const EdgeInsets.fromLTRB(16, 12, 16, 24),
children: [ children: [
_ProfileCard(profile: state.userProfile),
const SizedBox(height: 12),
_SectionCard( _SectionCard(
title: 'Preferences', title: 'Preferences',
children: [ children: [
@ -86,13 +86,6 @@ class _SettingsBody extends StatelessWidget {
title: const Text('Haptics'), title: const Text('Haptics'),
subtitle: const Text('Vibration feedback'), subtitle: const Text('Vibration feedback'),
), ),
const Divider(height: 1),
SwitchListTile.adaptive(
value: state.fingerEnabled,
onChanged: onFingerChanged,
title: const Text('Fingerprint'),
subtitle: const Text('Enable fingerprint authentication'),
),
], ],
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
@ -112,6 +105,134 @@ class _SettingsBody extends StatelessWidget {
} }
} }
class _ProfileCard extends StatelessWidget {
const _ProfileCard({required this.profile});
final UserProfile profile;
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme;
return Container(
decoration: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: colorScheme.outlineVariant.withValues(alpha: 0.5),
),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.06),
blurRadius: 14,
offset: const Offset(0, 6),
),
],
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Container(
width: 54,
height: 54,
decoration: BoxDecoration(
color: colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(18),
),
alignment: Alignment.center,
child: Text(
profile.initials,
style: textTheme.titleLarge?.copyWith(
color: colorScheme.onPrimaryContainer,
fontWeight: FontWeight.w900,
),
),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
profile.displayName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w800,
),
),
const SizedBox(height: 3),
Text(
'@${profile.username}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 10),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
_ProfileChip(
icon: Icons.badge_rounded,
label: profile.roleLabel,
),
_ProfileChip(
icon: Icons.store_rounded,
label: profile.branchLabel,
),
],
),
],
),
),
],
),
),
);
}
}
class _ProfileChip extends StatelessWidget {
const _ProfileChip({required this.icon, required this.label});
final IconData icon;
final String label;
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme;
return Container(
padding: const EdgeInsets.symmetric(horizontal: 9, vertical: 6),
decoration: BoxDecoration(
color: colorScheme.surfaceContainerHighest.withValues(alpha: 0.55),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, size: 15, color: colorScheme.onSurfaceVariant),
const SizedBox(width: 5),
Text(
label,
style: textTheme.labelMedium?.copyWith(
color: colorScheme.onSurfaceVariant,
fontWeight: FontWeight.w700,
),
),
],
),
);
}
}
class _LogoutButton extends StatelessWidget { class _LogoutButton extends StatelessWidget {
const _LogoutButton({required this.onPressed}); const _LogoutButton({required this.onPressed});
@ -157,10 +278,12 @@ class _SectionCard extends StatelessWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
color: colorScheme.surface, color: colorScheme.surface,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: Border.all(color: colorScheme.outlineVariant.withOpacity(0.5)), border: Border.all(
color: colorScheme.outlineVariant.withValues(alpha: 0.5),
),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.06), color: Colors.black.withValues(alpha: 0.06),
blurRadius: 14, blurRadius: 14,
offset: const Offset(0, 6), offset: const Offset(0, 6),
), ),

View File

@ -40,7 +40,6 @@ dependencies:
riverpod_annotation: ^4.0.2 riverpod_annotation: ^4.0.2
carousel_slider: ^5.1.2 carousel_slider: ^5.1.2
mobile_scanner: ^7.2.0 mobile_scanner: ^7.2.0
local_auth: ^3.0.1
shared_preferences: ^2.5.5 shared_preferences: ^2.5.5
dev_dependencies: dev_dependencies: