screen slide issue and add role
This commit is contained in:
parent
0102a7405c
commit
0d79d5a514
@ -1,5 +1,6 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../../shared/models/permission_matrix_models.dart';
|
||||
import '../../../../shared/models/user_management_models.dart';
|
||||
import '../../../roles/data/repositories/role_repository_impl.dart';
|
||||
import '../../../roles/domain/usecases/role_usecases.dart';
|
||||
@ -8,28 +9,64 @@ import '../../../roles/presentation/providers/roles_provider.dart';
|
||||
class RoleFormState {
|
||||
const RoleFormState({
|
||||
this.editingRole,
|
||||
this.permissionCatalog = const [],
|
||||
this.isSubmitting = false,
|
||||
this.errorMessage,
|
||||
});
|
||||
|
||||
final RoleCardModel? editingRole;
|
||||
final List<PermissionModuleCatalog> permissionCatalog;
|
||||
final bool isSubmitting;
|
||||
final String? errorMessage;
|
||||
|
||||
/// Modules that expose a view permission in the live catalog.
|
||||
List<PermissionModuleCatalog> get selectableModules => permissionCatalog
|
||||
.where((module) => module.permissionIdForAction('view') != null)
|
||||
.toList();
|
||||
|
||||
RoleFormState copyWith({
|
||||
RoleCardModel? editingRole,
|
||||
List<PermissionModuleCatalog>? permissionCatalog,
|
||||
bool? isSubmitting,
|
||||
String? errorMessage,
|
||||
bool clearError = false,
|
||||
}) {
|
||||
return RoleFormState(
|
||||
editingRole: editingRole ?? this.editingRole,
|
||||
permissionCatalog: permissionCatalog ?? this.permissionCatalog,
|
||||
isSubmitting: isSubmitting ?? this.isSubmitting,
|
||||
errorMessage: clearError ? null : errorMessage ?? this.errorMessage,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CreateRoleRequest buildCreateRoleRequest({
|
||||
required String name,
|
||||
required String description,
|
||||
required Map<String, bool> selectedModules,
|
||||
required List<PermissionModuleCatalog> catalog,
|
||||
}) {
|
||||
final viewModules = <String>[];
|
||||
final permissionIds = <int>[];
|
||||
|
||||
for (final module in catalog) {
|
||||
if (selectedModules[module.code] != true) continue;
|
||||
|
||||
final viewPermissionId = module.permissionIdForAction('view');
|
||||
if (viewPermissionId == null) continue;
|
||||
|
||||
viewModules.add(module.code);
|
||||
permissionIds.add(viewPermissionId);
|
||||
}
|
||||
|
||||
return CreateRoleRequest(
|
||||
name: name,
|
||||
description: description.isEmpty ? null : description,
|
||||
permissionIds: permissionIds,
|
||||
viewModules: viewModules,
|
||||
);
|
||||
}
|
||||
|
||||
final createRoleUseCaseProvider = Provider(
|
||||
(ref) => CreateRoleUseCase(ref.watch(roleRepositoryProvider)),
|
||||
);
|
||||
@ -46,7 +83,13 @@ final roleFormProvider =
|
||||
class RoleFormNotifier extends FamilyAsyncNotifier<RoleFormState, String?> {
|
||||
@override
|
||||
Future<RoleFormState> build(String? roleId) async {
|
||||
if (roleId == null) return const RoleFormState();
|
||||
if (roleId == null) {
|
||||
final catalogResult = await ref.read(getPermissionCatalogUseCaseProvider)();
|
||||
if (catalogResult.failure != null) throw catalogResult.failure!;
|
||||
return RoleFormState(
|
||||
permissionCatalog: catalogResult.data ?? const [],
|
||||
);
|
||||
}
|
||||
|
||||
final cached = ref.read(rolesListProvider).valueOrNull?.roles;
|
||||
var editingRole = cached?.where((role) => role.id == roleId).firstOrNull;
|
||||
|
||||
@ -3,12 +3,12 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../../core/errors/failure.dart';
|
||||
import '../../../../core/utils/validators.dart';
|
||||
import '../../../../shared/models/permission_matrix_models.dart';
|
||||
import '../../../../shared/models/user_management_models.dart';
|
||||
import '../../../../shared/widgets/app_button.dart';
|
||||
import '../../../../shared/widgets/app_loading_view.dart';
|
||||
import '../../../../shared/widgets/app_text_field.dart';
|
||||
import '../../../../shared/widgets/error_view.dart';
|
||||
import '../../domain/entities/rbac_entities.dart';
|
||||
import '../providers/role_form_provider.dart';
|
||||
import 'rbac_widgets.dart';
|
||||
|
||||
@ -27,10 +27,9 @@ class _RoleFormPanelState extends ConsumerState<RoleFormPanel> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _nameController = TextEditingController();
|
||||
final _descriptionController = TextEditingController();
|
||||
final Map<String, bool> _viewPermissions = {
|
||||
for (final module in rbacModules) module.key: false,
|
||||
};
|
||||
final Map<String, bool> _viewPermissions = {};
|
||||
bool _prefilled = false;
|
||||
bool _permissionsInitialized = false;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@ -49,6 +48,9 @@ class _RoleFormPanelState extends ConsumerState<RoleFormPanel> {
|
||||
Future<void> _save() async {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
|
||||
final formState = ref.read(roleFormProvider(widget.roleId)).valueOrNull;
|
||||
if (formState == null) return;
|
||||
|
||||
final notifier = ref.read(roleFormProvider(widget.roleId).notifier);
|
||||
final bool success;
|
||||
|
||||
@ -62,9 +64,11 @@ class _RoleFormPanelState extends ConsumerState<RoleFormPanel> {
|
||||
);
|
||||
} else {
|
||||
success = await notifier.submitCreate(
|
||||
CreateRoleRequest(
|
||||
buildCreateRoleRequest(
|
||||
name: _nameController.text.trim(),
|
||||
description: _descriptionController.text.trim(),
|
||||
selectedModules: _viewPermissions,
|
||||
catalog: formState.permissionCatalog,
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -124,6 +128,20 @@ class _RoleFormPanelState extends ConsumerState<RoleFormPanel> {
|
||||
onRetry: () => ref.invalidate(roleFormProvider(widget.roleId)),
|
||||
),
|
||||
data: (formState) {
|
||||
if (!widget.isEditing &&
|
||||
!_permissionsInitialized &&
|
||||
formState.selectableModules.isNotEmpty) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted || _permissionsInitialized) return;
|
||||
setState(() {
|
||||
_permissionsInitialized = true;
|
||||
for (final module in formState.selectableModules) {
|
||||
_viewPermissions.putIfAbsent(module.code, () => false);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (formState.editingRole != null && !_prefilled) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted || _prefilled) return;
|
||||
@ -167,16 +185,27 @@ class _RoleFormPanelState extends ConsumerState<RoleFormPanel> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
...rbacModules.map((module) {
|
||||
return ModulePermissionRow(
|
||||
icon: module.icon,
|
||||
color: module.color,
|
||||
label: module.label,
|
||||
value: _viewPermissions[module.key] ?? false,
|
||||
onChanged: (v) =>
|
||||
setState(() => _viewPermissions[module.key] = v),
|
||||
);
|
||||
}),
|
||||
if (formState.selectableModules.isEmpty)
|
||||
Text(
|
||||
'No view permissions available from the server.',
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
)
|
||||
else
|
||||
...formState.selectableModules.asMap().entries.map((entry) {
|
||||
final module = entry.value;
|
||||
final appearance =
|
||||
permissionModuleAppearance(module.code, entry.key);
|
||||
return ModulePermissionRow(
|
||||
icon: appearance.icon,
|
||||
color: appearance.color,
|
||||
label: module.name,
|
||||
value: _viewPermissions[module.code] ?? false,
|
||||
onChanged: (v) =>
|
||||
setState(() => _viewPermissions[module.code] = v),
|
||||
);
|
||||
}),
|
||||
],
|
||||
],
|
||||
),
|
||||
|
||||
@ -57,7 +57,8 @@ class RoleRemoteDataSource {
|
||||
}
|
||||
|
||||
Future<RoleModel> createRole(CreateRoleRequest request) async {
|
||||
final response = await dio.post(ApiEndpoints.roles, data: request.toJson());
|
||||
final payload = request.toJson()..removeWhere((_, value) => value == null);
|
||||
final response = await dio.post(ApiEndpoints.roles, data: payload);
|
||||
return _parseRole(response.data);
|
||||
}
|
||||
|
||||
|
||||
@ -10,6 +10,13 @@ const permissionMatrixActionOrder = [
|
||||
'export',
|
||||
];
|
||||
|
||||
class ModulePermissionEntry {
|
||||
const ModulePermissionEntry({required this.id, required this.action});
|
||||
|
||||
final String id;
|
||||
final String action;
|
||||
}
|
||||
|
||||
String permissionActionLabel(String action) => switch (action) {
|
||||
'view' => 'VIEW',
|
||||
'create' => 'CREATE',
|
||||
@ -32,25 +39,47 @@ class PermissionModuleCatalog {
|
||||
required this.code,
|
||||
required this.name,
|
||||
required this.actions,
|
||||
required this.permissions,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String code;
|
||||
final String name;
|
||||
final List<String> actions;
|
||||
final List<ModulePermissionEntry> permissions;
|
||||
|
||||
int? permissionIdForAction(String action) {
|
||||
final normalized = action.toLowerCase();
|
||||
for (final permission in permissions) {
|
||||
if (permission.action.toLowerCase() == normalized) {
|
||||
return int.tryParse(permission.id);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
factory PermissionModuleCatalog.fromJson(Map<String, dynamic> json) {
|
||||
final permissions = json['permissions'] as List<dynamic>? ?? const [];
|
||||
final actions = permissions
|
||||
.map((item) => (item as Map<String, dynamic>)['action'] as String?)
|
||||
.whereType<String>()
|
||||
.toList();
|
||||
final permissions = <ModulePermissionEntry>[];
|
||||
for (final item in json['permissions'] as List<dynamic>? ?? const []) {
|
||||
if (item is! Map<String, dynamic>) continue;
|
||||
final action = item['action'] as String?;
|
||||
if (action == null) continue;
|
||||
permissions.add(
|
||||
ModulePermissionEntry(
|
||||
id: item['id']?.toString() ?? '',
|
||||
action: action,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final actions = permissions.map((entry) => entry.action).toList();
|
||||
|
||||
return PermissionModuleCatalog(
|
||||
id: json['id']?.toString() ?? '',
|
||||
code: json['code'] as String? ?? '',
|
||||
name: json['name'] as String? ?? '',
|
||||
actions: sortPermissionActions(actions),
|
||||
permissions: permissions,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -182,9 +211,9 @@ class RolePermissionMatrix {
|
||||
'ROLES' => styles[1],
|
||||
'MASTERS' => styles[2],
|
||||
'VENDOR' => styles[3],
|
||||
'PURCHASE_ORDERS' || 'PO' => styles[4],
|
||||
'PURCHASE_ORDERS' || 'PURCHASE_ORDER' || 'PO' => styles[4],
|
||||
'GRN' => styles[5],
|
||||
'ASSETS' => styles[6],
|
||||
'ASSETS' || 'ASSET' || 'ASSET_MANAGEMENT' => styles[6],
|
||||
_ => styles[index % styles.length],
|
||||
};
|
||||
return byCode;
|
||||
|
||||
@ -231,6 +231,8 @@ class CreateRoleRequest with _$CreateRoleRequest {
|
||||
required String name,
|
||||
String? description,
|
||||
@JsonKey(name: 'is_active') @Default(true) bool isActive,
|
||||
@JsonKey(name: 'permission_ids') @Default([]) List<int> permissionIds,
|
||||
@JsonKey(name: 'view_modules') @Default([]) List<String> viewModules,
|
||||
}) = _CreateRoleRequest;
|
||||
|
||||
factory CreateRoleRequest.fromJson(Map<String, dynamic> json) =>
|
||||
|
||||
@ -3011,6 +3011,10 @@ mixin _$CreateRoleRequest {
|
||||
String? get description => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'is_active')
|
||||
bool get isActive => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'permission_ids')
|
||||
List<int> get permissionIds => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'view_modules')
|
||||
List<String> get viewModules => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this CreateRoleRequest to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@ -3033,6 +3037,8 @@ abstract class $CreateRoleRequestCopyWith<$Res> {
|
||||
String name,
|
||||
String? description,
|
||||
@JsonKey(name: 'is_active') bool isActive,
|
||||
@JsonKey(name: 'permission_ids') List<int> permissionIds,
|
||||
@JsonKey(name: 'view_modules') List<String> viewModules,
|
||||
});
|
||||
}
|
||||
|
||||
@ -3054,6 +3060,8 @@ class _$CreateRoleRequestCopyWithImpl<$Res, $Val extends CreateRoleRequest>
|
||||
Object? name = null,
|
||||
Object? description = freezed,
|
||||
Object? isActive = null,
|
||||
Object? permissionIds = null,
|
||||
Object? viewModules = null,
|
||||
}) {
|
||||
return _then(
|
||||
_value.copyWith(
|
||||
@ -3069,6 +3077,14 @@ class _$CreateRoleRequestCopyWithImpl<$Res, $Val extends CreateRoleRequest>
|
||||
? _value.isActive
|
||||
: isActive // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
permissionIds: null == permissionIds
|
||||
? _value.permissionIds
|
||||
: permissionIds // ignore: cast_nullable_to_non_nullable
|
||||
as List<int>,
|
||||
viewModules: null == viewModules
|
||||
? _value.viewModules
|
||||
: viewModules // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
)
|
||||
as $Val,
|
||||
);
|
||||
@ -3088,6 +3104,8 @@ abstract class _$$CreateRoleRequestImplCopyWith<$Res>
|
||||
String name,
|
||||
String? description,
|
||||
@JsonKey(name: 'is_active') bool isActive,
|
||||
@JsonKey(name: 'permission_ids') List<int> permissionIds,
|
||||
@JsonKey(name: 'view_modules') List<String> viewModules,
|
||||
});
|
||||
}
|
||||
|
||||
@ -3108,6 +3126,8 @@ class __$$CreateRoleRequestImplCopyWithImpl<$Res>
|
||||
Object? name = null,
|
||||
Object? description = freezed,
|
||||
Object? isActive = null,
|
||||
Object? permissionIds = null,
|
||||
Object? viewModules = null,
|
||||
}) {
|
||||
return _then(
|
||||
_$CreateRoleRequestImpl(
|
||||
@ -3123,6 +3143,14 @@ class __$$CreateRoleRequestImplCopyWithImpl<$Res>
|
||||
? _value.isActive
|
||||
: isActive // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
permissionIds: null == permissionIds
|
||||
? _value._permissionIds
|
||||
: permissionIds // ignore: cast_nullable_to_non_nullable
|
||||
as List<int>,
|
||||
viewModules: null == viewModules
|
||||
? _value._viewModules
|
||||
: viewModules // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -3135,7 +3163,10 @@ class _$CreateRoleRequestImpl implements _CreateRoleRequest {
|
||||
required this.name,
|
||||
this.description,
|
||||
@JsonKey(name: 'is_active') this.isActive = true,
|
||||
});
|
||||
@JsonKey(name: 'permission_ids') final List<int> permissionIds = const [],
|
||||
@JsonKey(name: 'view_modules') final List<String> viewModules = const [],
|
||||
}) : _permissionIds = permissionIds,
|
||||
_viewModules = viewModules;
|
||||
|
||||
factory _$CreateRoleRequestImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$CreateRoleRequestImplFromJson(json);
|
||||
@ -3147,10 +3178,27 @@ class _$CreateRoleRequestImpl implements _CreateRoleRequest {
|
||||
@override
|
||||
@JsonKey(name: 'is_active')
|
||||
final bool isActive;
|
||||
final List<int> _permissionIds;
|
||||
@override
|
||||
@JsonKey(name: 'permission_ids')
|
||||
List<int> get permissionIds {
|
||||
if (_permissionIds is EqualUnmodifiableListView) return _permissionIds;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_permissionIds);
|
||||
}
|
||||
|
||||
final List<String> _viewModules;
|
||||
@override
|
||||
@JsonKey(name: 'view_modules')
|
||||
List<String> get viewModules {
|
||||
if (_viewModules is EqualUnmodifiableListView) return _viewModules;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_viewModules);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CreateRoleRequest(name: $name, description: $description, isActive: $isActive)';
|
||||
return 'CreateRoleRequest(name: $name, description: $description, isActive: $isActive, permissionIds: $permissionIds, viewModules: $viewModules)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -3162,12 +3210,27 @@ class _$CreateRoleRequestImpl implements _CreateRoleRequest {
|
||||
(identical(other.description, description) ||
|
||||
other.description == description) &&
|
||||
(identical(other.isActive, isActive) ||
|
||||
other.isActive == isActive));
|
||||
other.isActive == isActive) &&
|
||||
const DeepCollectionEquality().equals(
|
||||
other._permissionIds,
|
||||
_permissionIds,
|
||||
) &&
|
||||
const DeepCollectionEquality().equals(
|
||||
other._viewModules,
|
||||
_viewModules,
|
||||
));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, name, description, isActive);
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
name,
|
||||
description,
|
||||
isActive,
|
||||
const DeepCollectionEquality().hash(_permissionIds),
|
||||
const DeepCollectionEquality().hash(_viewModules),
|
||||
);
|
||||
|
||||
/// Create a copy of CreateRoleRequest
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@ -3191,6 +3254,8 @@ abstract class _CreateRoleRequest implements CreateRoleRequest {
|
||||
required final String name,
|
||||
final String? description,
|
||||
@JsonKey(name: 'is_active') final bool isActive,
|
||||
@JsonKey(name: 'permission_ids') final List<int> permissionIds,
|
||||
@JsonKey(name: 'view_modules') final List<String> viewModules,
|
||||
}) = _$CreateRoleRequestImpl;
|
||||
|
||||
factory _CreateRoleRequest.fromJson(Map<String, dynamic> json) =
|
||||
@ -3203,6 +3268,12 @@ abstract class _CreateRoleRequest implements CreateRoleRequest {
|
||||
@override
|
||||
@JsonKey(name: 'is_active')
|
||||
bool get isActive;
|
||||
@override
|
||||
@JsonKey(name: 'permission_ids')
|
||||
List<int> get permissionIds;
|
||||
@override
|
||||
@JsonKey(name: 'view_modules')
|
||||
List<String> get viewModules;
|
||||
|
||||
/// Create a copy of CreateRoleRequest
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
|
||||
@ -228,6 +228,16 @@ _$CreateRoleRequestImpl _$$CreateRoleRequestImplFromJson(
|
||||
name: json['name'] as String,
|
||||
description: json['description'] as String?,
|
||||
isActive: json['is_active'] as bool? ?? true,
|
||||
permissionIds:
|
||||
(json['permission_ids'] as List<dynamic>?)
|
||||
?.map((e) => (e as num).toInt())
|
||||
.toList() ??
|
||||
const [],
|
||||
viewModules:
|
||||
(json['view_modules'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$CreateRoleRequestImplToJson(
|
||||
@ -236,6 +246,8 @@ Map<String, dynamic> _$$CreateRoleRequestImplToJson(
|
||||
'name': instance.name,
|
||||
'description': instance.description,
|
||||
'is_active': instance.isActive,
|
||||
'permission_ids': instance.permissionIds,
|
||||
'view_modules': instance.viewModules,
|
||||
};
|
||||
|
||||
_$UpdateRoleRequestImpl _$$UpdateRoleRequestImplFromJson(
|
||||
|
||||
@ -118,11 +118,13 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: RouteConstants.dashboard,
|
||||
builder: (context, state) => const DashboardScreen(),
|
||||
pageBuilder: (context, state) =>
|
||||
shellPage(state, const DashboardScreen()),
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.companies,
|
||||
builder: (context, state) => const CompanyListScreen(),
|
||||
pageBuilder: (context, state) =>
|
||||
shellPage(state, const CompanyListScreen()),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'add',
|
||||
@ -137,7 +139,8 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.branches,
|
||||
builder: (context, state) => const BranchListScreen(),
|
||||
pageBuilder: (context, state) =>
|
||||
shellPage(state, const BranchListScreen()),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'add',
|
||||
@ -152,14 +155,18 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.usersRoleManagement,
|
||||
builder: (context, state) => UsersRoleManagementScreen(
|
||||
initialTab: rbacTabFromLocation(state.uri.toString()),
|
||||
pageBuilder: (context, state) => shellPage(
|
||||
state,
|
||||
UsersRoleManagementScreen(
|
||||
initialTab: rbacTabFromLocation(state.uri.toString()),
|
||||
),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.users,
|
||||
builder: (context, state) => const UsersRoleManagementScreen(
|
||||
initialTab: RbacTab.users,
|
||||
pageBuilder: (context, state) => shellPage(
|
||||
state,
|
||||
const UsersRoleManagementScreen(initialTab: RbacTab.users),
|
||||
),
|
||||
routes: [
|
||||
GoRoute(
|
||||
@ -180,8 +187,9 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.roles,
|
||||
builder: (context, state) => const UsersRoleManagementScreen(
|
||||
initialTab: RbacTab.roles,
|
||||
pageBuilder: (context, state) => shellPage(
|
||||
state,
|
||||
const UsersRoleManagementScreen(initialTab: RbacTab.roles),
|
||||
),
|
||||
routes: [
|
||||
GoRoute(
|
||||
@ -194,11 +202,13 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.profile,
|
||||
builder: (context, state) => const UserProfileScreen(),
|
||||
pageBuilder: (context, state) =>
|
||||
shellPage(state, const UserProfileScreen()),
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.assets,
|
||||
builder: (context, state) => const AssetListScreen(),
|
||||
pageBuilder: (context, state) =>
|
||||
shellPage(state, const AssetListScreen()),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'add',
|
||||
@ -242,27 +252,33 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.departments,
|
||||
builder: (context, state) => const DepartmentsScreen(),
|
||||
pageBuilder: (context, state) =>
|
||||
shellPage(state, const DepartmentsScreen()),
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.locations,
|
||||
builder: (context, state) => const LocationsScreen(),
|
||||
pageBuilder: (context, state) =>
|
||||
shellPage(state, const LocationsScreen()),
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.uom,
|
||||
builder: (context, state) => const UomScreen(),
|
||||
pageBuilder: (context, state) =>
|
||||
shellPage(state, const UomScreen()),
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.reports,
|
||||
builder: (context, state) => const ReportsScreen(),
|
||||
pageBuilder: (context, state) =>
|
||||
shellPage(state, const ReportsScreen()),
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.auditLogs,
|
||||
builder: (context, state) => const AuditLogsScreen(),
|
||||
pageBuilder: (context, state) =>
|
||||
shellPage(state, const AuditLogsScreen()),
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.settings,
|
||||
builder: (context, state) => const SettingsScreen(),
|
||||
pageBuilder: (context, state) =>
|
||||
shellPage(state, const SettingsScreen()),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'general',
|
||||
@ -301,7 +317,8 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
if (DevConfig.screenPreviewEnabled)
|
||||
GoRoute(
|
||||
path: RouteConstants.screenGallery,
|
||||
builder: (context, state) => const ScreenGalleryScreen(),
|
||||
pageBuilder: (context, state) =>
|
||||
shellPage(state, const ScreenGalleryScreen()),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -319,6 +336,15 @@ Widget _themedRoute(GoRouterState state, Widget child) {
|
||||
);
|
||||
}
|
||||
|
||||
/// Instant swap for main shell pages — prevents old page showing through
|
||||
/// during sidebar navigation slide transitions.
|
||||
Page<void> shellPage(GoRouterState state, Widget child) {
|
||||
return NoTransitionPage<void>(
|
||||
key: state.pageKey,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
class _AuthListenable extends ChangeNotifier {
|
||||
_AuthListenable(this._ref) {
|
||||
_ref.listen(authStateProvider, (_, __) => notifyListeners());
|
||||
|
||||
@ -30,6 +30,20 @@ class _AppShellState extends ConsumerState<AppShell> {
|
||||
final _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
bool _sidebarCollapsed = false;
|
||||
|
||||
Widget _pageContent(BuildContext context, String currentRoute, Widget child) {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Material(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
child: ThemeKeyedSubtree(
|
||||
pageKey: currentRoute,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final authState = ref.watch(authStateProvider);
|
||||
@ -71,10 +85,7 @@ class _AppShellState extends ConsumerState<AppShell> {
|
||||
_scaffoldKey.currentState?.closeDrawer();
|
||||
},
|
||||
),
|
||||
body: ThemeKeyedSubtree(
|
||||
pageKey: currentRoute,
|
||||
child: widget.child,
|
||||
),
|
||||
body: _pageContent(context, currentRoute, widget.child),
|
||||
);
|
||||
}
|
||||
|
||||
@ -97,10 +108,7 @@ class _AppShellState extends ConsumerState<AppShell> {
|
||||
onItemTap: (route) => goAndDismissOverlays(context, route),
|
||||
),
|
||||
Expanded(
|
||||
child: ThemeKeyedSubtree(
|
||||
pageKey: currentRoute,
|
||||
child: widget.child,
|
||||
),
|
||||
child: _pageContent(context, currentRoute, widget.child),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -126,10 +134,7 @@ class _AppShellState extends ConsumerState<AppShell> {
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: ThemeKeyedSubtree(
|
||||
pageKey: currentRoute,
|
||||
child: widget.child,
|
||||
),
|
||||
child: _pageContent(context, currentRoute, widget.child),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user