feat: Adds session management and reception detail view

Updates the sign-in API endpoint to `/simpos/v1/sign_in`.
Captures the `set-cookie` header from successful sign-in responses and stores it as a `sessionId`.
Persists the session ID using secure storage and includes it in subsequent API requests via the `Cookie` header for improved session management.
Extends the `AuthModel` to include the new `sessionId` field.

Enables navigation from the reception list to a new reception details page, passing the selected reception's ID.
Refactors the `StockPickingCard` into a dedicated component and adds loading indicators to the reception list.
This commit is contained in:
mandreshope 2025-07-29 13:45:24 +03:00
parent 41a660db9a
commit 9435907c28
14 changed files with 344 additions and 129 deletions

View File

@ -44,7 +44,7 @@ class ApiCalls {
}) async {
try {
final response = await dioService.post(
path: '/sign_in',
path: '/simpos/v1/sign_in',
data: {
"params": {
"login": email,
@ -56,7 +56,14 @@ class ApiCalls {
if (response.statusCode == 200) {
final data = response.data;
if (data['result']['success'] == true) {
return Result.success(AuthModel.fromJson(data['result']['data']));
String? cookie;
if (response.headers.map.containsKey('set-cookie')) {
cookie = response.headers.map['set-cookie']?.firstOrNull;
}
final auth = AuthModel.fromJson(
data['result']['data'],
).copyWith(sessionId: cookie);
return Result.success(auth);
} else {
return Result.error(Error(data['result']['success']));
}

View File

@ -12,6 +12,7 @@ abstract class AuthModel with _$AuthModel {
String? refreshToken,
String? name,
String? username,
String? sessionId,
}) = _AuthModel;
factory AuthModel.fromJson(Map<String, dynamic> json) =>

View File

@ -16,7 +16,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$AuthModel {
@JsonKey(name: 'access_token') String? get accessToken;@JsonKey(name: 'db_name') String? get dbName; int? get uid; String? get refreshToken; String? get name; String? get username;
@JsonKey(name: 'access_token') String? get accessToken;@JsonKey(name: 'db_name') String? get dbName; int? get uid; String? get refreshToken; String? get name; String? get username; String? get sessionId;
/// Create a copy of AuthModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@ -27,16 +27,16 @@ $AuthModelCopyWith<AuthModel> get copyWith => _$AuthModelCopyWithImpl<AuthModel>
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is AuthModel&&(identical(other.accessToken, accessToken) || other.accessToken == accessToken)&&(identical(other.dbName, dbName) || other.dbName == dbName)&&(identical(other.uid, uid) || other.uid == uid)&&(identical(other.refreshToken, refreshToken) || other.refreshToken == refreshToken)&&(identical(other.name, name) || other.name == name)&&(identical(other.username, username) || other.username == username));
return identical(this, other) || (other.runtimeType == runtimeType&&other is AuthModel&&(identical(other.accessToken, accessToken) || other.accessToken == accessToken)&&(identical(other.dbName, dbName) || other.dbName == dbName)&&(identical(other.uid, uid) || other.uid == uid)&&(identical(other.refreshToken, refreshToken) || other.refreshToken == refreshToken)&&(identical(other.name, name) || other.name == name)&&(identical(other.username, username) || other.username == username)&&(identical(other.sessionId, sessionId) || other.sessionId == sessionId));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,accessToken,dbName,uid,refreshToken,name,username);
int get hashCode => Object.hash(runtimeType,accessToken,dbName,uid,refreshToken,name,username,sessionId);
@override
String toString() {
return 'AuthModel(accessToken: $accessToken, dbName: $dbName, uid: $uid, refreshToken: $refreshToken, name: $name, username: $username)';
return 'AuthModel(accessToken: $accessToken, dbName: $dbName, uid: $uid, refreshToken: $refreshToken, name: $name, username: $username, sessionId: $sessionId)';
}
@ -47,7 +47,7 @@ abstract mixin class $AuthModelCopyWith<$Res> {
factory $AuthModelCopyWith(AuthModel value, $Res Function(AuthModel) _then) = _$AuthModelCopyWithImpl;
@useResult
$Res call({
@JsonKey(name: 'access_token') String? accessToken,@JsonKey(name: 'db_name') String? dbName, int? uid, String? refreshToken, String? name, String? username
@JsonKey(name: 'access_token') String? accessToken,@JsonKey(name: 'db_name') String? dbName, int? uid, String? refreshToken, String? name, String? username, String? sessionId
});
@ -64,7 +64,7 @@ class _$AuthModelCopyWithImpl<$Res>
/// Create a copy of AuthModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? accessToken = freezed,Object? dbName = freezed,Object? uid = freezed,Object? refreshToken = freezed,Object? name = freezed,Object? username = freezed,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? accessToken = freezed,Object? dbName = freezed,Object? uid = freezed,Object? refreshToken = freezed,Object? name = freezed,Object? username = freezed,Object? sessionId = freezed,}) {
return _then(_self.copyWith(
accessToken: freezed == accessToken ? _self.accessToken : accessToken // ignore: cast_nullable_to_non_nullable
as String?,dbName: freezed == dbName ? _self.dbName : dbName // ignore: cast_nullable_to_non_nullable
@ -72,6 +72,7 @@ as String?,uid: freezed == uid ? _self.uid : uid // ignore: cast_nullable_to_non
as int?,refreshToken: freezed == refreshToken ? _self.refreshToken : refreshToken // ignore: cast_nullable_to_non_nullable
as String?,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String?,username: freezed == username ? _self.username : username // ignore: cast_nullable_to_non_nullable
as String?,sessionId: freezed == sessionId ? _self.sessionId : sessionId // ignore: cast_nullable_to_non_nullable
as String?,
));
}
@ -83,7 +84,7 @@ as String?,
@JsonSerializable(createToJson: false)
class _AuthModel implements AuthModel {
_AuthModel({@JsonKey(name: 'access_token') this.accessToken, @JsonKey(name: 'db_name') this.dbName, this.uid, this.refreshToken, this.name, this.username});
_AuthModel({@JsonKey(name: 'access_token') this.accessToken, @JsonKey(name: 'db_name') this.dbName, this.uid, this.refreshToken, this.name, this.username, this.sessionId});
factory _AuthModel.fromJson(Map<String, dynamic> json) => _$AuthModelFromJson(json);
@override@JsonKey(name: 'access_token') final String? accessToken;
@ -92,6 +93,7 @@ class _AuthModel implements AuthModel {
@override final String? refreshToken;
@override final String? name;
@override final String? username;
@override final String? sessionId;
/// Create a copy of AuthModel
/// with the given fields replaced by the non-null parameter values.
@ -103,16 +105,16 @@ _$AuthModelCopyWith<_AuthModel> get copyWith => __$AuthModelCopyWithImpl<_AuthMo
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AuthModel&&(identical(other.accessToken, accessToken) || other.accessToken == accessToken)&&(identical(other.dbName, dbName) || other.dbName == dbName)&&(identical(other.uid, uid) || other.uid == uid)&&(identical(other.refreshToken, refreshToken) || other.refreshToken == refreshToken)&&(identical(other.name, name) || other.name == name)&&(identical(other.username, username) || other.username == username));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AuthModel&&(identical(other.accessToken, accessToken) || other.accessToken == accessToken)&&(identical(other.dbName, dbName) || other.dbName == dbName)&&(identical(other.uid, uid) || other.uid == uid)&&(identical(other.refreshToken, refreshToken) || other.refreshToken == refreshToken)&&(identical(other.name, name) || other.name == name)&&(identical(other.username, username) || other.username == username)&&(identical(other.sessionId, sessionId) || other.sessionId == sessionId));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,accessToken,dbName,uid,refreshToken,name,username);
int get hashCode => Object.hash(runtimeType,accessToken,dbName,uid,refreshToken,name,username,sessionId);
@override
String toString() {
return 'AuthModel(accessToken: $accessToken, dbName: $dbName, uid: $uid, refreshToken: $refreshToken, name: $name, username: $username)';
return 'AuthModel(accessToken: $accessToken, dbName: $dbName, uid: $uid, refreshToken: $refreshToken, name: $name, username: $username, sessionId: $sessionId)';
}
@ -123,7 +125,7 @@ abstract mixin class _$AuthModelCopyWith<$Res> implements $AuthModelCopyWith<$Re
factory _$AuthModelCopyWith(_AuthModel value, $Res Function(_AuthModel) _then) = __$AuthModelCopyWithImpl;
@override @useResult
$Res call({
@JsonKey(name: 'access_token') String? accessToken,@JsonKey(name: 'db_name') String? dbName, int? uid, String? refreshToken, String? name, String? username
@JsonKey(name: 'access_token') String? accessToken,@JsonKey(name: 'db_name') String? dbName, int? uid, String? refreshToken, String? name, String? username, String? sessionId
});
@ -140,7 +142,7 @@ class __$AuthModelCopyWithImpl<$Res>
/// Create a copy of AuthModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? accessToken = freezed,Object? dbName = freezed,Object? uid = freezed,Object? refreshToken = freezed,Object? name = freezed,Object? username = freezed,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? accessToken = freezed,Object? dbName = freezed,Object? uid = freezed,Object? refreshToken = freezed,Object? name = freezed,Object? username = freezed,Object? sessionId = freezed,}) {
return _then(_AuthModel(
accessToken: freezed == accessToken ? _self.accessToken : accessToken // ignore: cast_nullable_to_non_nullable
as String?,dbName: freezed == dbName ? _self.dbName : dbName // ignore: cast_nullable_to_non_nullable
@ -148,6 +150,7 @@ as String?,uid: freezed == uid ? _self.uid : uid // ignore: cast_nullable_to_non
as int?,refreshToken: freezed == refreshToken ? _self.refreshToken : refreshToken // ignore: cast_nullable_to_non_nullable
as String?,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String?,username: freezed == username ? _self.username : username // ignore: cast_nullable_to_non_nullable
as String?,sessionId: freezed == sessionId ? _self.sessionId : sessionId // ignore: cast_nullable_to_non_nullable
as String?,
));
}

View File

@ -13,4 +13,5 @@ _AuthModel _$AuthModelFromJson(Map<String, dynamic> json) => _AuthModel(
refreshToken: json['refreshToken'] as String?,
name: json['name'] as String?,
username: json['username'] as String?,
sessionId: json['sessionId'] as String?,
);

View File

@ -5,3 +5,4 @@ export 'product_scanned_component.dart';
export 'outline_button_component.dart';
export 'quick_action_component.dart';
export 'main_appbar_component.dart';
export 'stock_picking_card_component.dart';

View File

@ -0,0 +1,81 @@
import 'package:barcode_scanner/themes/app_theme.dart';
import 'package:flutter/material.dart';
class StockPickingCard extends StatelessWidget {
const StockPickingCard({
super.key,
required this.reference,
required this.from,
required this.to,
required this.contact,
required this.origin,
required this.status,
});
final String? reference;
final String? from;
final String? to;
final String? contact;
final String? origin;
final String? status;
@override
Widget build(BuildContext context) {
return Card(
elevation: 3,
color: AppTheme.of(context).primaryBackground,
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(Icons.receipt_long, color: Colors.blue),
const SizedBox(width: 8),
Expanded(
child: Text(
reference ?? 'Référence inconnue',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(height: 12),
_infoRow(Icons.call_made, "De", from),
_infoRow(Icons.call_received, "Vers", to),
_infoRow(Icons.person, "Contact", contact),
_infoRow(Icons.insert_drive_file, "Document dorigine", origin),
_infoRow(Icons.check_circle, "Statut", status),
],
),
),
);
}
Widget _infoRow(IconData icon, String label, String? value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Row(
children: [
Icon(icon, size: 20, color: Colors.grey[700]),
const SizedBox(width: 8),
Text(
"$label : ",
style: const TextStyle(fontWeight: FontWeight.w600),
),
Expanded(
child: Text(
value ?? "-",
style: const TextStyle(color: Colors.black87),
),
),
],
),
);
}
}

View File

@ -66,7 +66,7 @@ class LoginPageModel extends StateNotifier<LoginPageState> {
final res = await ApiCalls.signIn(email: email, password: password);
res.when(
(auth) async {
setTokenInLocal(auth.accessToken ?? '', auth);
setTokenInLocal(auth);
onSuccess?.call();
},
(error) {
@ -87,10 +87,10 @@ class LoginPageModel extends StateNotifier<LoginPageState> {
}
}
Future<void> setTokenInLocal(String token, AuthModel auth) async {
Future<void> setTokenInLocal(AuthModel auth) async {
await Future.wait([
tokenProvider.setToken(token),
tokenProvider.setRefreshToken(token),
tokenProvider.setToken(auth.accessToken ?? ''),
tokenProvider.setRefreshToken(auth.accessToken ?? ''),
userConnectedProvider.set(
UserStruct(
id: auth.uid.toString(),
@ -102,7 +102,7 @@ class LoginPageModel extends StateNotifier<LoginPageState> {
]);
state = state.copyWith(loading: false, status: LoginPageStateStatus.logged);
debugPrint(token);
debugPrint(auth.accessToken);
}
Future<void> logOut() async {

View File

@ -1,3 +1,4 @@
export 'reception/reception_page.dart';
export 'delivery/delivery_page.dart';
export 'inventory/inventory_page.dart';
export 'reception/reception_details_page.dart';

View File

@ -0,0 +1,108 @@
import 'package:barcode_scanner/backend/schema/stock_picking/stock_picking_model.dart';
import 'package:barcode_scanner/components/components.dart';
import 'package:barcode_scanner/pages/operation/reception/reception_page_model.dart';
import 'package:barcode_scanner/themes/app_theme.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter/material.dart';
class ReceptionDetailsPage extends ConsumerStatefulWidget {
const ReceptionDetailsPage({super.key, required this.receptionId});
final int receptionId;
@override
ConsumerState<ReceptionDetailsPage> createState() =>
_ReceptionDetailsPageState();
}
class _ReceptionDetailsPageState extends ConsumerState<ReceptionDetailsPage> {
final globalKey = GlobalKey<ScaffoldState>();
@override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) {
ref.read(receptionPageModelProvider.notifier).getUserConnected();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppTheme.of(context).primaryBackground,
key: globalKey,
drawer: DrawerComponent(isOperationExpanded: true),
appBar: MainAppbarComponent(
scaffoledKey: globalKey,
title: "Réceptions",
subTitle: "Opérations d'Entrepôt",
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: ListView(
children: [
const SizedBox(height: 16),
QuickActionComponent(),
const SizedBox(height: 16),
Consumer(
builder: (_, WidgetRef ref, _) {
final state = ref.watch(receptionPageModelProvider);
if (state.loadingReceptions) {
return Center(child: LoadingProgressComponent());
}
return Card(
color: AppTheme.of(context).secondaryBackground,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(color: AppTheme.of(context).alternate),
),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
spacing: 10,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Réceptions',
style: AppTheme.of(
context,
).bodyMedium.copyWith(fontWeight: FontWeight.bold),
),
state.receptions?.result?.records?.isEmpty == true
? Text('No data')
: ListView.builder(
physics: NeverScrollableScrollPhysics(),
primary: true,
shrinkWrap: true,
itemCount:
(state.receptions?.result?.records ??
<StockPickingRecordModel>[])
.length,
itemBuilder: (context, index) {
final record =
state.receptions?.result?.records![index];
return GestureDetector(
onTap: () {},
child: StockPickingCard(
reference: record?.name ?? '',
from: record?.locationId?.completeName,
to: record?.locationDestId?.completeName,
contact: record?.partnerId?.displayName,
origin: record?.origin,
status: record?.state,
),
);
},
),
],
),
),
);
},
),
],
),
),
);
}
}

View File

@ -1,6 +1,7 @@
import 'package:barcode_scanner/backend/schema/stock_picking/stock_picking_model.dart';
import 'package:barcode_scanner/components/components.dart';
import 'package:barcode_scanner/pages/operation/reception/reception_page_model.dart';
import 'package:barcode_scanner/router/go_secure_router_builder.dart';
import 'package:barcode_scanner/themes/app_theme.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -25,7 +26,6 @@ class _ReceptionPageState extends ConsumerState<ReceptionPage> {
@override
Widget build(BuildContext context) {
final state = ref.watch(receptionPageModelProvider);
return Scaffold(
backgroundColor: AppTheme.of(context).primaryBackground,
key: globalKey,
@ -42,50 +42,71 @@ class _ReceptionPageState extends ConsumerState<ReceptionPage> {
const SizedBox(height: 16),
QuickActionComponent(),
const SizedBox(height: 16),
Card(
color: AppTheme.of(context).primaryBackground,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(color: AppTheme.of(context).alternate),
),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
spacing: 10,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Réceptions récentes',
style: AppTheme.of(
context,
).bodyMedium.copyWith(fontWeight: FontWeight.bold),
Consumer(
builder: (_, WidgetRef ref, _) {
final state = ref.watch(receptionPageModelProvider);
if (state.loadingReceptions) {
return Center(child: LoadingProgressComponent());
}
return Card(
color: AppTheme.of(context).secondaryBackground,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(color: AppTheme.of(context).alternate),
),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
spacing: 10,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Réceptions',
style: AppTheme.of(
context,
).bodyMedium.copyWith(fontWeight: FontWeight.bold),
),
state.receptions?.result?.records?.isEmpty == true
? Text('No data')
: ListView.builder(
physics: NeverScrollableScrollPhysics(),
primary: true,
shrinkWrap: true,
itemCount:
(state.receptions?.result?.records ??
<StockPickingRecordModel>[])
.length,
itemBuilder: (context, index) {
final reception =
state.receptions?.result?.records![index];
return GestureDetector(
onTap: () {
final id = reception?.id;
if (id == null) return;
ReceptionDetailsRoute(
receptionId: id,
).push(context);
},
child: StockPickingCard(
reference: reception?.name ?? '',
from: reception?.locationId?.completeName,
to: reception
?.locationDestId
?.completeName,
contact:
reception?.partnerId?.displayName,
origin: reception?.origin,
status: reception?.state,
),
);
},
),
],
),
state.receptions?.result?.records?.isEmpty == true
? Text('No data')
: ListView.builder(
primary: true,
shrinkWrap: true,
itemCount:
(state.receptions?.result?.records ??
<StockPickingRecordModel>[])
.length,
itemBuilder: (context, index) {
final record =
state.receptions?.result?.records![index];
return StockPickingCard(
reference: record?.name ?? '',
from: record?.locationId?.completeName,
to: record?.locationDestId?.completeName,
contact: record?.partnerId?.displayName,
origin: record?.origin,
status: record?.state,
);
},
),
],
),
),
),
);
},
),
],
),
@ -93,68 +114,3 @@ class _ReceptionPageState extends ConsumerState<ReceptionPage> {
);
}
}
class StockPickingCard extends StatelessWidget {
const StockPickingCard({
super.key,
required this.reference,
required this.from,
required this.to,
required this.contact,
required this.origin,
required this.status,
});
final String? reference;
final String? from;
final String? to;
final String? contact;
final String? origin;
final String? status;
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
reference ?? 'Référence inconnue',
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
_infoRow("De", from),
_infoRow("Vers", to),
_infoRow("Contact", contact),
_infoRow("Document dorigine", origin),
_infoRow("Statut", status),
],
),
),
);
}
Widget _infoRow(String label, String? value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 2.0),
child: Row(
children: [
Text(
"$label : ",
style: const TextStyle(fontWeight: FontWeight.w600),
),
Expanded(
child: Text(
value ?? "-",
style: const TextStyle(color: Colors.black87),
),
),
],
),
);
}
}

View File

@ -20,6 +20,7 @@ final appSecureRoutes = $appRoutes;
TypedGoRoute<ReceptionRoute>(path: 'ReceptionPage'),
TypedGoRoute<DeliveryRoute>(path: 'DeliveryPage'),
TypedGoRoute<InventoryRoute>(path: 'InventoryPage'),
TypedGoRoute<ReceptionDetailsRoute>(path: 'ReceptionDetailsPage'),
],
)
class SecureRoute extends GoRouteData with _$SecureRoute {
@ -110,3 +111,12 @@ class InventoryRoute extends GoRouteData with _$InventoryRoute {
child: InventoryPage(),
);
}
class ReceptionDetailsRoute extends GoRouteData with _$ReceptionDetailsRoute {
const ReceptionDetailsRoute({required this.receptionId});
final int receptionId;
@override
Widget build(BuildContext context, GoRouterState state) =>
ReceptionDetailsPage(receptionId: receptionId);
}

View File

@ -40,6 +40,11 @@ RouteBase get $secureRoute => GoRouteData.$route(
factory: _$InventoryRoute._fromState,
),
GoRouteData.$route(
path: 'ReceptionDetailsPage',
factory: _$ReceptionDetailsRoute._fromState,
),
],
);
@ -211,3 +216,31 @@ mixin _$InventoryRoute on GoRouteData {
@override
void replace(BuildContext context) => context.replace(location);
}
mixin _$ReceptionDetailsRoute on GoRouteData {
static ReceptionDetailsRoute _fromState(GoRouterState state) =>
ReceptionDetailsRoute(
receptionId: int.parse(state.uri.queryParameters['reception-id']!)!,
);
ReceptionDetailsRoute get _self => this as ReceptionDetailsRoute;
@override
String get location => GoRouteData.$location(
'/SecurePage/ReceptionDetailsPage',
queryParams: {'reception-id': _self.receptionId.toString()},
);
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}

View File

@ -11,7 +11,7 @@ import 'package:flutter/widgets.dart';
class DioService {
DioService({bool addAuthorization = false, required this.tokenProvider}) {
_options = BaseOptions(
baseUrl: AppConstants.baseUrl,
baseUrl: AppConstants.domain,
connectTimeout: const Duration(seconds: 45),
receiveTimeout: const Duration(seconds: 45),
headers: {
@ -37,8 +37,10 @@ class DioService {
InterceptorsWrapper(
onRequest: (options, handler) async {
final token = await tokenProvider.getToken();
final sessionId = await tokenProvider.getSessionId();
if (token != null) {
options.headers.addAll({"Authorization": "Bearer $token"});
options.headers.addAll({"Cookie": sessionId});
debugPrint('Authorization ${options.headers["Authorization"]}');
final url = Uri.parse(
"${options.baseUrl}${options.path}",

View File

@ -15,12 +15,17 @@ class TokenProvider {
TokenProvider(this._storage);
final FlutterSecureStorage _storage;
static const String tokenKey = "tokenKey";
static const String sessionIdKey = "sessionIdKey";
static const String refreshTokenKey = "refreshTokenKey";
Future<void> setToken(String token) async {
return _storage.write(key: tokenKey, value: token);
}
Future<void> setSessionId(String sessionId) async {
return _storage.write(key: sessionIdKey, value: sessionId);
}
Future<void> setRefreshToken(String token) async {
return _storage.write(key: refreshTokenKey, value: token);
}
@ -32,6 +37,11 @@ class TokenProvider {
return token;
}
Future<String?> getSessionId() async {
final value = await _storage.read(key: sessionIdKey);
return value;
}
Future<String?> getRefreshToken() async {
final refreshToken = await _storage.read(key: refreshTokenKey);
debugPrint("Refresh token: $refreshToken");
@ -42,6 +52,7 @@ class TokenProvider {
Future<void> deleteToken() async {
await _storage.delete(key: tokenKey);
await _storage.delete(key: refreshTokenKey);
await _storage.delete(key: sessionIdKey);
}
}