diff --git a/android/gradle.properties b/android/gradle.properties
index 4617f96d772a363abed31166843abc0fc2c01732..32638a39cd697ca58b0dd482eb1cd46e5e051809 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,5 +1,5 @@
 org.gradle.jvmargs=-Xmx1536M
 android.useAndroidX=true
 android.enableJetifier=true
-app.versionName=0.0.34
-app.versionCode=34
+app.versionName=0.0.35
+app.versionCode=35
diff --git a/assets/translations/en.json b/assets/translations/en.json
index 6b82e28719f34bf4b043f9414da9ef18fb13bd43..6b7214cae0652de42dcf91671facd5518f05afa4 100644
--- a/assets/translations/en.json
+++ b/assets/translations/en.json
@@ -5,6 +5,7 @@
   "bottom_nav_home": "Home",
   "bottom_nav_discoveries": "Discoveries",
   "bottom_nav_repartition": "Statistics",
+  "bottom_nav_settings": "Settings",
 
   "global_statistics": "Global statistics",
   "statistics_total_scrobbles_count": "Total scrobbles count: {count}",
@@ -23,6 +24,11 @@
 
   "top_artists_title": "Top artists ({daysCount} days)",
 
+  "settings_title": "Settings",
+  "settings_label_username": "Username: ",
+  "settings_label_security_token": "Security token: ",
+  "settings_button_save": "Save",
+
   "MON": "MON",
   "TUE": "TUE",
   "WED": "WED",
diff --git a/assets/translations/fr.json b/assets/translations/fr.json
index 84b42363ca5567aaae73d4e7a84fc84cf192d47e..b495f443ab3e8d7cebd8159e0564771744033379 100644
--- a/assets/translations/fr.json
+++ b/assets/translations/fr.json
@@ -5,6 +5,7 @@
   "bottom_nav_home": "Accueil",
   "bottom_nav_discoveries": "Découvertes",
   "bottom_nav_repartition": "Statistiques",
+  "bottom_nav_settings": "Paramètres",
 
   "global_statistics": "Statistiques globales d'écoutes",
   "statistics_total_scrobbles_count": "Nombre total d'écoutes : {count}",
@@ -23,6 +24,11 @@
 
   "top_artists_title": "Top artistes ({daysCount} jours)",
 
+  "settings_title": "Paramètres",
+  "settings_label_username": "Utilisateur : ",
+  "settings_label_security_token": "Jeton de sécurité : ",
+  "settings_button_save": "Enregistrer",
+
   "MON": "LUN",
   "TUE": "MAR",
   "WED": "MER",
diff --git a/fastlane/metadata/android/en-US/changelogs/35.txt b/fastlane/metadata/android/en-US/changelogs/35.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5a6ab6b957dd25817c5c1921d71b710bd41e0cc8
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/35.txt
@@ -0,0 +1 @@
+Add settings page (username, security token).
diff --git a/fastlane/metadata/android/fr-FR/changelogs/35.txt b/fastlane/metadata/android/fr-FR/changelogs/35.txt
new file mode 100644
index 0000000000000000000000000000000000000000..84037e39ff0fbb7c7b8f4000d0daf3a9dae7953b
--- /dev/null
+++ b/fastlane/metadata/android/fr-FR/changelogs/35.txt
@@ -0,0 +1 @@
+Ajout d'une page de paramètres (nom d'utilisateur, jeton de sécurité).
diff --git a/lib/cubit/bottom_nav_cubit.dart b/lib/cubit/bottom_nav_cubit.dart
index 8753255ac7acba27b594ea5cfb5b46aafee31625..1592a438fa2446d66db1a35519bd675d7f2dd146 100644
--- a/lib/cubit/bottom_nav_cubit.dart
+++ b/lib/cubit/bottom_nav_cubit.dart
@@ -3,7 +3,7 @@ import 'package:hydrated_bloc/hydrated_bloc.dart';
 class BottomNavCubit extends HydratedCubit<int> {
   BottomNavCubit() : super(0);
 
-  int pagesCount = 3;
+  int pagesCount = 4;
 
   void updateIndex(int index) {
     if (isIndexAllowed(index)) {
diff --git a/lib/cubit/settings_cubit.dart b/lib/cubit/settings_cubit.dart
new file mode 100644
index 0000000000000000000000000000000000000000..45df233828485d0dd6df7b4399f3cdc09b67071b
--- /dev/null
+++ b/lib/cubit/settings_cubit.dart
@@ -0,0 +1,46 @@
+import 'package:equatable/equatable.dart';
+import 'package:flutter/material.dart';
+import 'package:hydrated_bloc/hydrated_bloc.dart';
+
+part 'settings_state.dart';
+
+class SettingsCubit extends HydratedCubit<SettingsState> {
+  SettingsCubit() : super(const SettingsState());
+
+  String getUsername() {
+    return state.username ?? '';
+  }
+
+  String getSecurityToken() {
+    return state.securityToken ?? '';
+  }
+
+  void setValues({
+    String? username,
+    String? securityToken,
+  }) {
+    emit(SettingsState(
+      username: username != null ? username : state.username,
+      securityToken: securityToken != null ? securityToken : state.securityToken,
+    ));
+  }
+
+  @override
+  SettingsState? fromJson(Map<String, dynamic> json) {
+    String username = json['username'] as String;
+    String securityToken = json['securityToken'] as String;
+
+    return SettingsState(
+      username: username,
+      securityToken: securityToken,
+    );
+  }
+
+  @override
+  Map<String, String>? toJson(SettingsState state) {
+    return <String, String>{
+      'username': state.username ?? '',
+      'securityToken': state.securityToken ?? '',
+    };
+  }
+}
diff --git a/lib/cubit/settings_state.dart b/lib/cubit/settings_state.dart
new file mode 100644
index 0000000000000000000000000000000000000000..c32e6bb60c484a7539a52a2cb1d2bc3c27b5f967
--- /dev/null
+++ b/lib/cubit/settings_state.dart
@@ -0,0 +1,23 @@
+part of 'settings_cubit.dart';
+
+@immutable
+class SettingsState extends Equatable {
+  const SettingsState({
+    this.username,
+    this.securityToken,
+  });
+
+  final String? username;
+  final String? securityToken;
+
+  @override
+  List<String?> get props => <String?>[
+        username,
+        securityToken,
+      ];
+
+  Map<String, String?> get values => <String, String?>{
+        'username': username,
+        'securityToken': securityToken,
+      };
+}
diff --git a/lib/main.dart b/lib/main.dart
index 52ae6ef743ac43935bfb2a373427b4a32b82950e..d4ca87cf3c8e94138b5fa45e851b5c143fa6ce1f 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -16,6 +16,7 @@ import 'package:scrobbles/cubit/data_statistics_global_cubit.dart';
 import 'package:scrobbles/cubit/data_statistics_recent_cubit.dart';
 import 'package:scrobbles/cubit/data_timeline_cubit.dart';
 import 'package:scrobbles/cubit/data_top_artists_cubit.dart';
+import 'package:scrobbles/cubit/settings_cubit.dart';
 import 'package:scrobbles/ui/skeleton.dart';
 
 void main() async {
@@ -49,6 +50,7 @@ class MyApp extends StatelessWidget {
   Widget build(BuildContext context) {
     return MultiBlocProvider(
       providers: [
+        BlocProvider<SettingsCubit>(create: (context) => SettingsCubit()),
         BlocProvider<BottomNavCubit>(create: (context) => BottomNavCubit()),
         BlocProvider<DataCountsByDayCubit>(create: (context) => DataCountsByDayCubit()),
         BlocProvider<DataCountsByHourCubit>(create: (context) => DataCountsByHourCubit()),
diff --git a/lib/ui/screens/settings.dart b/lib/ui/screens/settings.dart
new file mode 100644
index 0000000000000000000000000000000000000000..2dc9cec7f67713bf5f6f966eca30dbc60dc7442d
--- /dev/null
+++ b/lib/ui/screens/settings.dart
@@ -0,0 +1,23 @@
+import 'package:flutter/material.dart';
+
+import 'package:scrobbles/ui/widgets/header_app.dart';
+import 'package:scrobbles/ui/widgets/settings_form.dart';
+
+class ScreenSettings extends StatelessWidget {
+  const ScreenSettings({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      mainAxisAlignment: MainAxisAlignment.start,
+      crossAxisAlignment: CrossAxisAlignment.start,
+      mainAxisSize: MainAxisSize.max,
+      children: <Widget>[
+        SizedBox(height: 8),
+        AppHeader(text: 'settings_title'),
+        SizedBox(height: 8),
+        SettingsForm(),
+      ],
+    );
+  }
+}
diff --git a/lib/ui/skeleton.dart b/lib/ui/skeleton.dart
index fe99383846d2e450b605a8e70b100dd0602e1f6e..46c449c208958e368280f2deb58f355176ca7d65 100644
--- a/lib/ui/skeleton.dart
+++ b/lib/ui/skeleton.dart
@@ -5,6 +5,7 @@ import 'package:flutter_swipe/flutter_swipe.dart';
 import 'package:scrobbles/cubit/bottom_nav_cubit.dart';
 import 'package:scrobbles/ui/screens/discoveries.dart';
 import 'package:scrobbles/ui/screens/home.dart';
+import 'package:scrobbles/ui/screens/settings.dart';
 import 'package:scrobbles/ui/screens/statistics.dart';
 import 'package:scrobbles/ui/widgets/app_bar.dart';
 import 'package:scrobbles/ui/widgets/bottom_nav_bar.dart';
@@ -23,6 +24,7 @@ class _SkeletonScreenState extends State<SkeletonScreen> {
       const ScreenHome(),
       const ScreenDiscoveries(),
       const ScreenStatistics(),
+      const ScreenSettings(),
     ];
 
     return Scaffold(
diff --git a/lib/ui/widgets/bottom_nav_bar.dart b/lib/ui/widgets/bottom_nav_bar.dart
index daaed377dc3b2c28f8bf1a9cf0d0927396552e81..c775a6ac79e7ff50eabd40ae2bca30fb9913174c 100644
--- a/lib/ui/widgets/bottom_nav_bar.dart
+++ b/lib/ui/widgets/bottom_nav_bar.dart
@@ -54,6 +54,10 @@ class BottomNavBar extends StatelessWidget {
                 icon: const Icon(Ionicons.bar_chart_outline),
                 label: tr('bottom_nav_repartition'),
               ),
+              BottomNavigationBarItem(
+                icon: const Icon(Ionicons.settings_outline),
+                label: tr('bottom_nav_settings'),
+              ),
             ],
           );
         },
diff --git a/lib/ui/widgets/settings_form.dart b/lib/ui/widgets/settings_form.dart
new file mode 100644
index 0000000000000000000000000000000000000000..5af5f14463616d8371faec9efbdf3f3fcc7e2129
--- /dev/null
+++ b/lib/ui/widgets/settings_form.dart
@@ -0,0 +1,76 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:unicons/unicons.dart';
+
+import 'package:scrobbles/cubit/settings_cubit.dart';
+
+class SettingsForm extends StatefulWidget {
+  const SettingsForm({super.key});
+
+  @override
+  State<SettingsForm> createState() => _SettingsFormState();
+}
+
+class _SettingsFormState extends State<SettingsForm> {
+  final usernameController = TextEditingController();
+  final securityTokenController = TextEditingController();
+
+  @override
+  void dispose() {
+    usernameController.dispose();
+    securityTokenController.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    SettingsCubit settings = BlocProvider.of<SettingsCubit>(context);
+
+    usernameController.text = settings.getUsername();
+    securityTokenController.text = settings.getSecurityToken();
+
+    void saveSettings() {
+      BlocProvider.of<SettingsCubit>(context).setValues(
+        username: usernameController.text,
+        securityToken: securityTokenController.text,
+      );
+    }
+
+    return Column(
+      mainAxisAlignment: MainAxisAlignment.start,
+      crossAxisAlignment: CrossAxisAlignment.start,
+      mainAxisSize: MainAxisSize.max,
+      children: <Widget>[
+        Text('settings_label_username').tr(),
+        TextFormField(
+          controller: usernameController,
+          decoration: InputDecoration(
+            border: UnderlineInputBorder(),
+          ),
+        ),
+        SizedBox(height: 16),
+        Text('settings_label_security_token').tr(),
+        TextFormField(
+          controller: securityTokenController,
+          decoration: InputDecoration(
+            border: UnderlineInputBorder(),
+          ),
+        ),
+        SizedBox(height: 20),
+        ElevatedButton(
+          child: Row(
+            mainAxisAlignment: MainAxisAlignment.center,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              Icon(UniconsLine.save),
+              SizedBox(width: 8),
+              Text('settings_button_save').tr(),
+            ],
+          ),
+          onPressed: () => saveSettings(),
+        ),
+      ],
+    );
+  }
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index 8c8dc91094df1092d75a2305961405c996af1fae..8a873de9f3a12fc15e37335882e096d2bf0bac6b 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -3,7 +3,7 @@ description: Display scrobbles data and charts
 
 publish_to: 'none'
 
-version: 0.0.34+34
+version: 0.0.35+35
 
 environment:
   sdk: '^3.0.0'