[client] Basic profile dialog

This commit is contained in:
Abhishek Shroff
2025-07-18 13:55:39 +05:30
parent 049a844b8b
commit fe72865a46
5 changed files with 166 additions and 55 deletions
@@ -1,4 +1,3 @@
import 'package:circular_profile_avatar/circular_profile_avatar.dart';
import 'package:flutter/material.dart';
import 'package:phylum/libphylum/phylum_account.dart';
import 'package:phylum/ui/explorer/explorer_controller.dart';
@@ -10,7 +9,8 @@ import 'package:phylum/ui/layout/search.dart';
import 'package:phylum/ui/layout/sync_status_button.dart';
import 'package:phylum/ui/menu/bottom_sheet.dart';
import 'package:phylum/ui/menu/option_groups.dart';
import 'package:phylum/util/avatar.dart';
import 'package:phylum/ui/profile/profile_dialog.dart';
import 'package:phylum/ui/profile/profile_view.dart';
import 'package:provider/provider.dart';
class ExplorerPageCollapsed extends StatelessWidget {
@@ -79,30 +79,11 @@ class ExplorerPageCollapsed extends StatelessWidget {
StreamBuilder(
stream: account.userNotifier.stream,
initialData: account.user,
builder: (context, snapshot) {
final data = snapshot.data!;
String initials = data.name.split(' ').map((s) => s.isEmpty ? null : s.substring(0, 1)).join('');
if (initials.isEmpty) {
initials = data.email.substring(0, 2);
}
final colorScheme = getColorFromString(data.email);
return Padding(
padding: const EdgeInsets.all(6.0),
child: CircularProfileAvatar(
'',
initialsText: Text(
initials,
style: TextStyle(fontWeight: FontWeight.bold),
),
foregroundColor: colorScheme.onPrimaryContainer,
backgroundColor: colorScheme.primaryContainer,
borderWidth: 1,
borderColor: colorScheme.onPrimaryContainer,
radius: 18,
),
);
}),
builder: (context, snapshot) => ProfileView(
name: snapshot.data!.name,
email: snapshot.data!.email,
onTap: () => ProfileDialog.show(context),
)),
],
);
}
-22
View File
@@ -2,7 +2,6 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_state_notifier/flutter_state_notifier.dart';
import 'package:offtheline/offtheline.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:phylum/libphylum/user_permission.dart';
import 'package:phylum/ui/app/shortcuts.dart';
import 'package:phylum/libphylum/db/db.dart';
@@ -166,27 +165,6 @@ class NavList extends StatelessWidget {
: SizedBox();
}),
if (!kIsWeb) const DownloadButton(),
ListTile(
leading: const Icon(Icons.help),
title: const Text('About'),
onTap: () async {
Scaffold.maybeOf(context)?.closeDrawer();
final info = await PackageInfo.fromPlatform();
if (!context.mounted) return;
showAboutDialog(
context: context,
applicationVersion: '${info.packageName} - ${info.version} (${info.buildNumber})',
);
},
),
ListTile(
leading: const Icon(Icons.logout),
title: const Text('Log Out'),
onTap: () {
Scaffold.maybeOf(context)?.closeDrawer();
context.read<PhylumAccount>().close(clearData: true);
},
),
],
),
),
+110
View File
@@ -0,0 +1,110 @@
import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:phylum/libphylum/phylum_account.dart';
import 'package:phylum/ui/profile/profile_view.dart';
import 'package:phylum/util/dialogs.dart';
import 'package:provider/provider.dart';
class ProfileDialog extends StatelessWidget {
const ProfileDialog._();
static void show(BuildContext context) {
final account = context.read<PhylumAccount>();
showDialog(
context: context,
builder: (context) => MultiProvider(
providers: [
Provider.value(value: account),
],
child: AlertDialog(
scrollable: true,
content: SizedBox(width: 360, child: ProfileDialog._()),
actions: [
ElevatedButton(onPressed: Navigator.of(context).pop, child: Text('OK')),
],
),
),
);
}
@override
Widget build(BuildContext context) {
final account = context.read<PhylumAccount>();
return Column(
children: [
Card(
child: Column(
children: [
ListTile(
leading: ProfileView(name: account.user.name, email: account.user.email),
title: Row(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
fit: FlexFit.loose,
child: Text(
account.userRepository.getUserDisplayName(account.user.id),
maxLines: 1,
overflow: TextOverflow.fade,
softWrap: false,
),
),
IconButton(
icon: Icon(Icons.edit, size: 18),
splashRadius: 6,
padding: EdgeInsets.symmetric(vertical: 3, horizontal: 6),
constraints: BoxConstraints(),
onPressed: () async {
final name = await showInputDialog(
context,
title: 'Change Name',
labelText: 'Name',
preset: account.user.name,
);
if (name == null) return;
},
),
],
),
subtitle: Text(context.read<PhylumAccount>().apiClient.serverUrl.host),
),
Divider(thickness: 2),
const ListTile(
visualDensity: VisualDensity.compact,
leading: Icon(Icons.password),
title: Text('Change Password'),
),
ListTile(
visualDensity: VisualDensity.compact,
leading: const Icon(Icons.logout),
title: const Text('Log Out'),
onTap: () {
Navigator.of(context).pop();
context.read<PhylumAccount>().close(clearData: true);
},
),
],
),
),
const ListTile(
visualDensity: VisualDensity.compact,
leading: Icon(Icons.settings),
title: Text('Settings'),
),
ListTile(
visualDensity: VisualDensity.compact,
leading: const Icon(Icons.help),
title: const Text('About'),
onTap: () async {
final info = await PackageInfo.fromPlatform();
if (!context.mounted) return;
showAboutDialog(
context: context,
applicationVersion: '${info.packageName} - ${info.version} (${info.buildNumber})',
);
},
),
],
);
}
}
+49
View File
@@ -0,0 +1,49 @@
import 'package:circular_profile_avatar/circular_profile_avatar.dart';
import 'package:flutter/material.dart';
class ProfileView extends StatelessWidget {
final String name;
final String email;
final Function()? onTap;
const ProfileView({super.key, required this.name, required this.email, this.onTap});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(6.0),
child: onTap == null
? _buildAvatar()
: InkWell(
onTap: onTap,
child: _buildAvatar(),
),
);
}
Widget _buildAvatar() {
String initials = name.split(' ').map((s) => s.isEmpty ? null : s.substring(0, 1)).join('');
if (initials.isEmpty) {
initials = email.substring(0, 2);
}
final colorScheme = getColorFromString(email);
return CircularProfileAvatar(
'',
initialsText: Text(
initials,
style: TextStyle(fontWeight: FontWeight.bold),
),
foregroundColor: colorScheme.onPrimaryContainer,
backgroundColor: colorScheme.primaryContainer,
borderWidth: 1,
borderColor: colorScheme.onPrimaryContainer,
radius: 18,
);
}
}
ColorScheme getColorFromString(String input) {
return ColorScheme.fromSeed(
seedColor: Colors.primaries[(input.hashCode) % Colors.primaries.length],
);
}
-7
View File
@@ -1,7 +0,0 @@
import 'package:flutter/material.dart';
ColorScheme getColorFromString(String input) {
return ColorScheme.fromSeed(
seedColor: Colors.primaries[(input.hashCode) % Colors.primaries.length],
);
}