serverpod_admin_client 1.0.6
serverpod_admin_client: ^1.0.6 copied to clipboard
The missing admin dashboard for Serverpod. A complete Flutter package providing CRUD operations, foreign key relationships, boolean fields, date pickers, pagination, search, and authentication for man [...]
Serverpod Admin โ The Missing Admin Panel for Serverpod ๐ฏ #
Finally, an admin panel that Serverpod deserves!
You've built an amazing Serverpod app with powerful endpoints, robust models, and a beautiful frontend. But when it comes to managing your data, you're stuck writing custom endpoints, building one-off admin pages, or worseโdirectly accessing the database.
That's where Serverpod Admin comes in. This is the missing piece that transforms your Serverpod backend into a fully manageable system.
๐ Admin Dashboard Overview #
Secure login screen for admin users. Only users with the serverpod.admin scope can access the dashboard.

Browse and manage all your data with a beautiful, intuitive interface.

New record with a clean, user-friendly interface.

Powerful search and filtering capabilities to find exactly what you need.

Edit record with a clean, user-friendly interface.

Delete record with a clean, user-friendly interface.

View detailed information about any record.

Beautiful empty states when no records are found.

โจ Why Serverpod Admin Matters #
๐ Zero Configuration, Maximum Power #
No more writing boilerplate CRUD endpoints. Register your models once, and instantly get a complete admin interface with:
- Browse & Search โ Navigate through all your data with powerful filtering
- Create & Edit โ Intuitive forms for managing records
- Delete โ Safe deletion with proper validation
- Pagination โ Handle large datasets effortlessly
๐จ Frontend-Agnostic Architecture #
Built with flexibility in mind. The serverpod_admin_server exposes a clean API that any frontend can consume. Start with Flutter today, switch to Jaspr tomorrow, or build your own custom admin UIโthe choice is yours!
โก Built for Serverpod Developers #
- Type-Safe โ Leverages Serverpod's generated protocol classes
- Integrated โ Works seamlessly with your existing Serverpod setup
- Extensible โ Designed to grow with your needs
๐ ๏ธ Developer Experience First #
Stop spending days building admin interfaces. Get back to building features that matter. With Serverpod Admin, you can have a production-ready admin panel in minutes, not weeks.
๐ฆ Installation #
Server Side #
Run:
flutter pub get serverpod_admin_server
Flutter (Frontend) #
Run:
flutter pub get serverpod_admin_dashboard
That's it! You're good to go! ๐
๐งฉ Quick Start #
Registering Models (Server Side) #
import 'package:serverpod_admin_server/serverpod_admin_server.dart' as admin;
import 'package:use_serverpod_admin_server/src/generated/protocol.dart';
void registerAdminModule() {
admin.configureAdminModule((registry) {
registry.register<Post>();
registry.register<Person>();
registry.register<Comment>();
registry.register<Setting>();
// Add any model you want to manage!
});
}
Call registerAdminModule() in your server.dart file just after pod.start()
Setting Up Authentication #
Serverpod Admin uses serverpod_auth_idp for authentication. Make sure you have it configured in your server:
pod.initializeAuthServices(
tokenManagerBuilders: [
JwtConfigFromPasswords(),
],
identityProviderBuilders: [
EmailIdpConfigFromPasswords(
sendRegistrationVerificationCode: _sendRegistrationCode,
sendPasswordResetVerificationCode: _sendPasswordResetCode,
),
],
);
Creating an Admin User #
To access the admin panel, users must have the serverpod.admin scope. Here's how to create an admin user:
import 'package:serverpod/serverpod.dart';
import 'package:serverpod_auth_idp_server/core.dart';
import 'package:serverpod_auth_idp_server/providers/email.dart';
Future<void> findOrCreateAndLinkEmail() async {
// Create a manual session for internal work
var session = await Serverpod.instance.createSession();
// Use a nullable ID or UuidValue to track the target user
UuidValue? authUserId;
try {
final emailAdmin = AuthServices.instance.emailIdp.admin;
const email = 'admin@example.com';
const password = 'your-secure-password';
// 1. Check if the email account already exists
final emailAccount = await emailAdmin.findAccount(
session,
email: email,
);
if (emailAccount == null) {
// 2. Create a new AuthUser if no account exists
final authUser = await AuthServices.instance.authUsers.create(session);
authUserId = authUser.id;
// 3. Create the email authentication for the new user
await emailAdmin.createEmailAuthentication(
session,
authUserId: authUserId,
email: email,
password: password,
);
} else {
// If account exists, get the ID from the existing record
authUserId = emailAccount.authUserId;
}
// 4. Update the user to have admin scopes using the identified ID
await AuthServices.instance.authUsers.update(
session,
authUserId: authUserId,
scopes: {Scope.admin},
);
print("User $email updated to admin successfully.");
} catch (e) {
print("Error creating internal admin: $e");
} finally {
// IMPORTANT: Always close manual sessions to prevent memory leaks
await session.close();
}
}
Call findOrCreateAndLinkEmail() in your server.dart file after pod.start() to create your first admin user.
Using the Admin Dashboard (Flutter) #
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
client = Client('http://localhost:8080/')
..connectivityMonitor = FlutterConnectivityMonitor()
..authSessionManager = FlutterAuthSessionManager();
client.auth.initialize();
runApp(AdminDashboard(client: client));
}
That's it! You now have a fully working admin panel with authentication for your Serverpod app! ๐๐
The admin dashboard will automatically show a login screen for unauthenticated users. Only users with the serverpod.admin scope can access the admin panel.
๐จ Customization #
Serverpod Admin offers flexible customization options, from simple sidebar tweaks to complete UI replacement.
Sidebar Level Customization #
Customize individual sidebar items with custom labels and icons:
AdminDashboard(
client: client,
sidebarItemCustomizations: {
'posts': SidebarItemCustomization(
label: 'Posts',
icon: Icons.post_add,
),
'persons': SidebarItemCustomization(
label: 'Person',
icon: Icons.person,
),
'comments': SidebarItemCustomization(
label: 'Comment',
icon: Icons.comment,
),
'settings': SidebarItemCustomization(
label: 'Setting',
icon: Icons.settings,
),
},
)
This allows you to:
- Customize labels โ Change the display name for any resource
- Customize icons โ Use your own icons for better visual identification
- Keep it simple โ Only customize what you need, leave the rest default
Full Customization #
For complete control over the admin interface, you can replace any component with your own custom widgets:
AdminDashboard(
client: client,
// Custom sidebar - completely replace the default sidebar
customSidebarBuilder: (context, controller) {
return CustomSidebar(controller: controller);
},
// Custom body/records pane - replace the default table view
customBodyBuilder: (context, controller, operations) {
return CustomBody(
controller: controller,
operations: operations,
);
},
// Custom record details view
customDetailsBuilder: (context, controller, operations, resource, record) {
return CustomDetails(
controller: controller,
operations: operations,
resource: resource,
record: record,
);
},
// Custom edit dialog
customEditDialogBuilder: (context, controller, operations, resource,
currentValues, onSubmit) {
return CustomEditDialog(
resource: resource,
currentValues: currentValues,
onSubmit: onSubmit,
);
},
// Custom delete confirmation dialog
customDeleteDialogBuilder: (context, controller, operations, resource,
record, onConfirm) {
return CustomDeleteDialog(
resource: resource,
record: record,
onConfirm: onConfirm,
);
},
// Custom create dialog
customCreateDialogBuilder: (context, controller, operations, resource,
onSubmit) {
return CustomCreateDialog(
resource: resource,
onSubmit: onSubmit,
);
},
// Custom footer (displayed above the default footer)
customFooterBuilder: (context, controller) {
return CustomFooter(controller: controller);
},
// Custom themes
lightTheme: myLightTheme,
darkTheme: myDarkTheme,
initialThemeMode: ThemeMode.dark,
)
Available Customization Options
| Builder | Purpose | Parameters |
|---|---|---|
customSidebarBuilder |
Replace the entire sidebar | (context, controller) |
customBodyBuilder |
Replace the records table view | (context, controller, operations) |
customDetailsBuilder |
Replace the record details view | (context, controller, operations, resource, record) |
customEditDialogBuilder |
Replace the edit dialog | (context, controller, operations, resource, currentValues, onSubmit) |
customDeleteDialogBuilder |
Replace the delete confirmation dialog | (context, controller, operations, resource, record, onConfirm) |
customCreateDialogBuilder |
Replace the create dialog | (context, controller, operations, resource, onSubmit) |
customFooterBuilder |
Add custom footer above default footer | (context, controller) |
Custom Builder Guidelines
When creating custom builders, you have access to:
-
AdminDashboardControllerโ Provides access to:resourcesโ List of all registered resourcesselectedResourceโ Currently selected resourceloadingโ Loading statesthemeModeโ Current theme mode- Methods to load data, refresh, etc.
-
HomeOperationsโ Provides CRUD operations:list()โ Get list of recordsfind()โ Find a specific recordcreate()โ Create a new recordupdate()โ Update an existing recorddelete()โ Delete a record
-
AdminResourceโ Information about the resource:keyโ Resource identifiertableNameโ Database table namecolumnsโ List of column definitions
Example: Custom Sidebar
Widget CustomSidebar(AdminDashboardController controller) {
return Drawer(
child: ListView(
children: [
const DrawerHeader(
decoration: BoxDecoration(color: Colors.blue),
child: Text('My Admin Panel'),
),
...controller.resources.map((resource) {
final customization = controller.sidebarItemCustomizations?[resource.key];
return ListTile(
leading: Icon(customization?.icon ?? Icons.table_chart),
title: Text(customization?.label ?? resource.tableName),
selected: controller.selectedResource?.key == resource.key,
onTap: () => controller.selectResource(resource),
);
}),
],
),
);
}
Example: Custom Edit Dialog
Widget CustomEditDialog({
required AdminResource resource,
required Map<String, String> currentValues,
required Future<bool> Function(Map<String, String> payload) onSubmit,
}) {
final formKey = GlobalKey<FormState>();
final controllers = currentValues.map(
(key, value) => MapEntry(key, TextEditingController(text: value)),
);
return AlertDialog(
title: Text('Edit ${resource.tableName}'),
content: Form(
key: formKey,
child: SingleChildScrollView(
child: Column(
children: resource.columns.map((column) {
return TextFormField(
controller: controllers[column.name],
decoration: InputDecoration(labelText: column.name),
enabled: !column.isId, // Disable editing ID fields
);
}).toList(),
),
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel'),
),
TextButton(
onPressed: () async {
if (formKey.currentState!.validate()) {
final payload = controllers.map(
(key, controller) => MapEntry(key, controller.text),
);
final success = await onSubmit(payload);
if (success && context.mounted) {
Navigator.pop(context);
}
}
},
child: const Text('Save'),
),
],
);
}
๐ Security & Access Control #
Role-Based Access Control #
Serverpod Admin implements strict role-based access control:
- โ
Admin-Only Access โ By default, all access requires the
serverpod.adminscope - โ Secure by Default โ Without admin privileges, users cannot access any part of the admin panel
- โ Authentication Required โ The dashboard automatically shows a login screen for unauthenticated users
- โ
Scope Validation โ Users must have the
serverpod.adminscope to access any admin functionality
Important: Without the serverpod.admin scope, users will see an error message and cannot access the admin panel, even if they successfully authenticate with email/password.
Login Flow #
- User enters email and password on the login screen
- Authentication is handled via
serverpod_auth_idp(EmailAuthController) - Upon successful authentication, the system checks for the
serverpod.adminscope - If the user has admin scope, they're granted access to the dashboard
- If the user lacks admin scope, they see an error: "User does not have admin privileges"
๐ฎ What's Next? #
This is a proof of concept that's already stable and production-ready. We're actively working on:
- โ Export/Import โ Data portability built-in
- โ Role-Based Access โ Secure your admin panel (โ Implemented)
- โ Comprehensive Testing โ Ensuring reliability
๐ก The Vision #
Serverpod Admin fills the gap that every Serverpod developer has felt. No more custom admin code. No more database dives. Just a beautiful, powerful admin panel that works out of the box.
Welcome to the future of Serverpod administration. ๐