improve settings view

This commit is contained in:
otsmr 2025-02-02 23:19:43 +01:00
parent d13de25f96
commit 6a342380b3
8 changed files with 105 additions and 127 deletions

View file

@ -8,16 +8,15 @@ import 'package:logging/logging.dart';
import 'package:twonly/src/providers/download_change_provider.dart'; import 'package:twonly/src/providers/download_change_provider.dart';
import 'package:twonly/src/providers/messages_change_provider.dart'; import 'package:twonly/src/providers/messages_change_provider.dart';
import 'package:twonly/src/providers/contacts_change_provider.dart'; import 'package:twonly/src/providers/contacts_change_provider.dart';
import 'package:twonly/src/providers/settings_change_provider.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
import 'src/app.dart'; import 'src/app.dart';
import 'src/settings/settings_controller.dart';
import 'src/settings/settings_service.dart';
late DbProvider dbProvider; late DbProvider dbProvider;
late ApiProvider apiProvider; late ApiProvider apiProvider;
void main() async { void main() async {
final settingsController = SettingsController(SettingsService()); final settingsController = SettingsChangeProvider();
// Load the user's preferred theme while the splash screen is displayed. // Load the user's preferred theme while the splash screen is displayed.
// This prevents a sudden theme change when the app is first displayed. // This prevents a sudden theme change when the app is first displayed.
@ -62,8 +61,9 @@ void main() async {
ChangeNotifierProvider(create: (_) => MessagesChangeProvider()), ChangeNotifierProvider(create: (_) => MessagesChangeProvider()),
ChangeNotifierProvider(create: (_) => DownloadChangeProvider()), ChangeNotifierProvider(create: (_) => DownloadChangeProvider()),
ChangeNotifierProvider(create: (_) => ContactChangeProvider()), ChangeNotifierProvider(create: (_) => ContactChangeProvider()),
ChangeNotifierProvider(create: (_) => settingsController),
], ],
child: MyApp(settingsController: settingsController), child: MyApp(),
), ),
); );
} }

View file

@ -3,6 +3,7 @@ import 'package:twonly/main.dart';
import 'package:twonly/src/providers/contacts_change_provider.dart'; import 'package:twonly/src/providers/contacts_change_provider.dart';
import 'package:twonly/src/providers/download_change_provider.dart'; import 'package:twonly/src/providers/download_change_provider.dart';
import 'package:twonly/src/providers/messages_change_provider.dart'; import 'package:twonly/src/providers/messages_change_provider.dart';
import 'package:twonly/src/providers/settings_change_provider.dart';
import 'package:twonly/src/utils/storage.dart'; import 'package:twonly/src/utils/storage.dart';
import 'package:twonly/src/views/onboarding_view.dart'; import 'package:twonly/src/views/onboarding_view.dart';
import 'package:twonly/src/views/home_view.dart'; import 'package:twonly/src/views/home_view.dart';
@ -11,7 +12,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'dart:async'; import 'dart:async';
import 'settings/settings_controller.dart';
// these global function can be called from anywhere to update // these global function can be called from anywhere to update
// the ui when something changed. The callbacks will be set by // the ui when something changed. The callbacks will be set by
@ -27,9 +27,7 @@ Function(List<int>, bool) globalCallBackOnDownloadChange = (a, b) {};
/// The Widget that configures your application. /// The Widget that configures your application.
class MyApp extends StatefulWidget { class MyApp extends StatefulWidget {
const MyApp({super.key, required this.settingsController}); const MyApp({super.key});
final SettingsController settingsController;
@override @override
State<MyApp> createState() => _MyAppState(); State<MyApp> createState() => _MyAppState();
@ -114,7 +112,7 @@ class _MyAppState extends State<MyApp> {
// The ListenableBuilder Widget listens to the SettingsController for changes. // The ListenableBuilder Widget listens to the SettingsController for changes.
// Whenever the user updates their settings, the MaterialApp is rebuilt. // Whenever the user updates their settings, the MaterialApp is rebuilt.
return ListenableBuilder( return ListenableBuilder(
listenable: widget.settingsController, listenable: context.watch<SettingsChangeProvider>(),
builder: (BuildContext context, Widget? child) { builder: (BuildContext context, Widget? child) {
return MaterialApp( return MaterialApp(
restorationScopeId: 'app', restorationScopeId: 'app',
@ -141,7 +139,7 @@ class _MyAppState extends State<MyApp> {
inputDecorationTheme: inputDecorationTheme:
const InputDecorationTheme(border: OutlineInputBorder()), const InputDecorationTheme(border: OutlineInputBorder()),
), ),
themeMode: widget.settingsController.themeMode, themeMode: context.watch<SettingsChangeProvider>().themeMode,
home: Stack( home: Stack(
children: [ children: [
FutureBuilder<bool>( FutureBuilder<bool>(
@ -149,9 +147,7 @@ class _MyAppState extends State<MyApp> {
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasData) { if (snapshot.hasData) {
return snapshot.data! return snapshot.data!
? HomeView( ? HomeView()
settingsController: widget.settingsController,
)
: _showOnboarding : _showOnboarding
? OnboardingView( ? OnboardingView(
callbackOnSuccess: () { callbackOnSuccess: () {

View file

@ -1,17 +1,10 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:twonly/src/services/settings_service.dart';
import 'settings_service.dart'; class SettingsChangeProvider with ChangeNotifier, DiagnosticableTreeMixin {
/// A class that many Widgets can interact with to read user settings, update
/// user settings, or listen to user settings changes.
///
/// Controllers glue Data Services to Flutter Widgets. The SettingsController
/// uses the SettingsService to store and retrieve user settings.
class SettingsController with ChangeNotifier {
SettingsController(this._settingsService);
// Make SettingsService a private variable so it is not used directly. // Make SettingsService a private variable so it is not used directly.
final SettingsService _settingsService; final SettingsService _settingsService = SettingsService();
// Make ThemeMode a private variable so it is not updated directly without // Make ThemeMode a private variable so it is not updated directly without
// also persisting the changes with the SettingsService. // also persisting the changes with the SettingsService.

View file

@ -1,58 +0,0 @@
import 'package:flutter/material.dart';
import 'settings_controller.dart';
/// Displays the various settings that can be customized by the user.
///
/// When a user changes a setting, the SettingsController is updated and
/// Widgets that listen to the SettingsController are rebuilt.
class SettingsView extends StatelessWidget {
const SettingsView({required this.controller});
final SettingsController controller;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Settings'),
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
// Glue the SettingsController to the theme selection DropdownButton.
//
// When a user selects a theme from the dropdown list, the
// SettingsController is updated, which rebuilds the MaterialApp.
child: DropdownButton<ThemeMode>(
// Read the selected themeMode from the controller
value: controller.themeMode,
// Call the updateThemeMode method any time the user selects a theme.
onChanged: controller.updateThemeMode,
items: const [
DropdownMenuItem(
value: ThemeMode.system,
child: Text('System Theme'),
),
DropdownMenuItem(
value: ThemeMode.light,
child: Text('Light Theme'),
),
DropdownMenuItem(
value: ThemeMode.dark,
child: Text('Dark Theme'),
)
],
),
),
ElevatedButton(
onPressed: () {
showLicensePage(context: context);
},
child: Text('Show Licenses'),
),
],
));
}
}

View file

@ -1,3 +1,4 @@
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:twonly/src/components/flame.dart'; import 'package:twonly/src/components/flame.dart';
import 'package:twonly/src/components/initialsavatar.dart'; import 'package:twonly/src/components/initialsavatar.dart';
@ -14,6 +15,7 @@ import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/views/chat_item_details_view.dart'; import 'package:twonly/src/views/chat_item_details_view.dart';
import 'package:twonly/src/views/home_view.dart'; import 'package:twonly/src/views/home_view.dart';
import 'package:twonly/src/views/media_viewer_view.dart'; import 'package:twonly/src/views/media_viewer_view.dart';
import 'package:twonly/src/views/profile_view.dart';
import 'package:twonly/src/views/search_username_view.dart'; import 'package:twonly/src/views/search_username_view.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -70,12 +72,23 @@ class _ChatListViewState extends State<ChatListView> {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(AppLocalizations.of(context)!.chatsTitle), title: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProfileView(),
),
);
},
child: Text("twonly"),
),
// title:
actions: [ actions: [
NotificationBadge( NotificationBadge(
count: context.watch<ContactChangeProvider>().newContactRequests, count: context.watch<ContactChangeProvider>().newContactRequests,
child: IconButton( child: IconButton(
icon: Icon(Icons.person_add), icon: FaIcon(FontAwesomeIcons.userPlus, size: 18),
onPressed: () { onPressed: () {
Navigator.push( Navigator.push(
context, context,
@ -85,6 +98,17 @@ class _ChatListViewState extends State<ChatListView> {
); );
}, },
), ),
),
IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProfileView(),
),
);
},
icon: FaIcon(FontAwesomeIcons.gear, size: 19),
) )
], ],
), ),

View file

@ -2,15 +2,12 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:pie_menu/pie_menu.dart'; import 'package:pie_menu/pie_menu.dart';
import 'camera_preview_view.dart'; import 'camera_preview_view.dart';
import 'chat_list_view.dart'; import 'chat_list_view.dart';
import 'profile_view.dart';
import '../settings/settings_controller.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
Function(int) globalUpdateOfHomeViewPageIndex = (a) {}; Function(int) globalUpdateOfHomeViewPageIndex = (a) {};
class HomeView extends StatefulWidget { class HomeView extends StatefulWidget {
const HomeView({super.key, required this.settingsController}); const HomeView({super.key});
final SettingsController settingsController;
@override @override
State<HomeView> createState() => HomeViewState(); State<HomeView> createState() => HomeViewState();
@ -71,7 +68,7 @@ class HomeViewState extends State<HomeView> {
children: [ children: [
ChatListView(), ChatListView(),
CameraPreviewViewPermission(), CameraPreviewViewPermission(),
ProfileView(settingsController: widget.settingsController) // ProfileView(settingsController: widget.settingsController)
], ],
), ),
bottomNavigationBar: BottomNavigationBar( bottomNavigationBar: BottomNavigationBar(
@ -86,8 +83,8 @@ class HomeViewState extends State<HomeView> {
icon: FaIcon(FontAwesomeIcons.camera), icon: FaIcon(FontAwesomeIcons.camera),
label: "", label: "",
), ),
BottomNavigationBarItem( // BottomNavigationBarItem(
icon: FaIcon(FontAwesomeIcons.userShield), label: ""), // icon: FaIcon(FontAwesomeIcons.userShield), label: ""),
], ],
onTap: (int index) { onTap: (int index) {
activePageIdx = index; activePageIdx = index;

View file

@ -1,14 +1,14 @@
import 'package:provider/provider.dart';
import 'package:twonly/src/model/json/user_data.dart'; import 'package:twonly/src/model/json/user_data.dart';
import 'package:restart_app/restart_app.dart'; import 'package:restart_app/restart_app.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:twonly/src/settings/settings_controller.dart'; import 'package:twonly/src/providers/settings_change_provider.dart';
import 'package:twonly/src/settings/settings_view.dart';
import 'package:twonly/src/utils/storage.dart'; import 'package:twonly/src/utils/storage.dart';
class ProfileView extends StatefulWidget { class ProfileView extends StatefulWidget {
const ProfileView({super.key, required this.settingsController}); const ProfileView({super.key});
final SettingsController settingsController; // final SettingsController settingsController;
@override @override
State<ProfileView> createState() => _ProfileViewState(); State<ProfileView> createState() => _ProfileViewState();
@ -25,28 +25,52 @@ class _ProfileViewState extends State<ProfileView> {
future: _userData, future: _userData,
builder: (context, snap) { builder: (context, snap) {
if (snap.hasData) { if (snap.hasData) {
return Text("Hello ${snap.data!.username}!"); return Text("Settings");
// return Text("Hello ${snap.data!.username}!");
} else { } else {
return Container(); return Container();
} }
}),
actions: [
IconButton(
icon: const Icon(Icons.settings),
onPressed: () {
// Navigate to the settings page. If the user leaves and returns
// to the app after it has been killed while running in the
// background, the navigation stack is restored.
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
SettingsView(controller: widget.settingsController)),
);
}, },
), ),
]), ),
body: FilledButton.icon( body: Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
// Glue the SettingsController to the theme selection DropdownButton.
//
// When a user selects a theme from the dropdown list, the
// SettingsController is updated, which rebuilds the MaterialApp.
child: DropdownButton<ThemeMode>(
// Read the selected themeMode from the controller
value: context.watch<SettingsChangeProvider>().themeMode,
// Call the updateThemeMode method any time the user selects a theme.
onChanged: (theme) {
context.read<SettingsChangeProvider>().updateThemeMode(theme);
},
items: const [
DropdownMenuItem(
value: ThemeMode.system,
child: Text('System Theme'),
),
DropdownMenuItem(
value: ThemeMode.light,
child: Text('Light Theme'),
),
DropdownMenuItem(
value: ThemeMode.dark,
child: Text('Dark Theme'),
)
],
),
),
ElevatedButton(
onPressed: () {
showLicensePage(context: context);
},
child: Text('Show Licenses'),
),
FilledButton.icon(
onPressed: () async { onPressed: () async {
await deleteLocalUserData(); await deleteLocalUserData();
Restart.restartApp( Restart.restartApp(
@ -55,7 +79,9 @@ class _ProfileViewState extends State<ProfileView> {
); );
}, },
label: Text("Logout"), label: Text("Logout"),
icon: Icon(Icons.no_accounts)), icon: Icon(Icons.no_accounts),
); ),
],
));
} }
} }