Build a user activity audit trail by logging every significant action (login, create, update, delete) to a Firestore activity_log collection. A reusable Custom Action called at the end of every major Action Flow creates activity documents with userId, action type, resource details, and timestamp. Display logs in an admin ListView with date range, user, and action type filters. Give users their own activity timeline showing recent actions. A scheduled Cloud Function auto-deletes entries older than 90 days for privacy compliance and cost management.
Building a User Activity Audit Log in FlutterFlow
Activity logs create an audit trail of who did what and when in your app. They are essential for security auditing, debugging, compliance, and understanding user behavior. This tutorial builds a complete activity logging system with a reusable logging action, admin dashboard with rich filtering, user-facing activity timeline, and automated data retention.
Prerequisites
- A FlutterFlow project with Firestore and Firebase Authentication configured
- An existing app with Action Flows you want to log (CRUD operations, login, etc.)
- Cloud Functions enabled for the data retention cleanup job
- An admin role in your users collection for gating the admin log viewer
Step-by-step guide
Set up the Firestore data model for the activity log
Set up the Firestore data model for the activity log
Create an activity_log collection with fields: userId (String), userEmail (String, denormalized for admin display), action (String: 'login', 'page_view', 'create', 'update', 'delete'), resourceType (String: 'post', 'order', 'profile', 'comment', 'settings'), resourceId (String, the document ID of the affected resource), description (String, human-readable summary), metadata (Map, optional extra context), timestamp (Timestamp). Add Firestore indexes for: action + timestamp descending, userId + timestamp descending, and resourceType + timestamp descending to support all filter combinations.
Expected result: Firestore has an activity_log collection with indexes ready for efficient filtered queries.
Create a reusable logActivity Custom Action
Create a reusable logActivity Custom Action
Create a Custom Action logActivity that accepts parameters: action (String), resourceType (String), resourceId (String), description (String), metadata (Map, optional). Inside, it creates a new activity_log document with: userId from currentUser.uid, userEmail from currentUser.email, the passed parameters, and timestamp as FieldValue.serverTimestamp(). This single Custom Action is called at the end of every significant Action Flow throughout the app. For example, after creating a post: call logActivity(action: 'create', resourceType: 'post', resourceId: newPostId, description: 'Created post: [title]').
1// Custom Action: logActivity2import 'package:cloud_firestore/cloud_firestore.dart';3import 'package:firebase_auth/firebase_auth.dart';45Future<void> logActivity({6 required String action,7 required String resourceType,8 required String resourceId,9 required String description,10 Map<String, dynamic>? metadata,11}) async {12 final user = FirebaseAuth.instance.currentUser;13 if (user == null) return;1415 await FirebaseFirestore.instance.collection('activity_log').add({16 'userId': user.uid,17 'userEmail': user.email,18 'action': action,19 'resourceType': resourceType,20 'resourceId': resourceId,21 'description': description,22 'metadata': metadata ?? {},23 'timestamp': FieldValue.serverTimestamp(),24 });25}Expected result: A reusable Custom Action that can be called from any Action Flow to log the activity.
Integrate logging into existing Action Flows
Integrate logging into existing Action Flows
Add the logActivity Custom Action at the end of every significant Action Flow in your app. After login success: logActivity(action: 'login', resourceType: 'session', resourceId: currentUser.uid, description: 'User logged in'). After creating a document: logActivity(action: 'create', resourceType: 'post', resourceId: newDoc.id, description: 'Created post: title'). After updating: logActivity(action: 'update', ...). After deleting: logActivity(action: 'delete', ...). For sensitive actions (settings changes, password reset), include additional context in the metadata Map (e.g., which field changed). Do NOT log page_view events to Firestore as they generate too many writes; log only create, update, delete, and login actions.
Expected result: All significant user actions are automatically logged to Firestore as they happen.
Build the admin activity log viewer with filters
Build the admin activity log viewer with filters
Create an AdminActivityLogPage (Conditional Visibility: user role == 'admin'). Add a filter Row at the top with: DateTimePicker for start date, DateTimePicker for end date, DropDown for action type (All, Login, Create, Update, Delete), DropDown for resource type (All, Post, Order, Profile), and a TextField for user search. Below, add a ListView with Backend Query on activity_log filtered by the selected criteria, ordered by timestamp descending, with pagination (20 items per page). Each row shows: action type Icon (colored: green for create, blue for update, red for delete, grey for login), userEmail Text, description Text, resourceType Badge, and timestamp. Tap a row to show full details including metadata in a Bottom Sheet.
Expected result: An admin dashboard showing all user activities with date range, action type, and resource type filters.
Create the user-facing activity timeline
Create the user-facing activity timeline
On the user profile or account page, add a 'Your Recent Activity' section. Use a ListView with Backend Query on activity_log where userId == currentUser.uid, ordered by timestamp descending, limited to 20. Each row shows a timeline-style layout: a vertical line Container on the left with a colored dot Icon per action type, the description Text, resource type Text (small, grey), and relative timestamp Text ('2 hours ago', 'Yesterday'). Use a Custom Function to convert timestamps to relative time strings. Add a 'View All' button that navigates to a full activity history page with the same filters as the admin view but pre-filtered to the current user.
Expected result: Users can see a timeline of their own recent actions on their profile page.
Implement automatic data retention with a scheduled Cloud Function
Implement automatic data retention with a scheduled Cloud Function
Create a scheduled Cloud Function cleanupActivityLog that runs daily. It queries activity_log documents where timestamp < 90 days ago, batches them into groups of 500, and deletes each batch. This keeps the collection at a manageable size, reduces Firestore costs, and complies with data retention policies. Log the cleanup results (number of deleted documents) to a separate system_logs collection for monitoring. Adjust the 90-day retention period based on your compliance requirements.
1// Cloud Function: cleanupActivityLog (scheduled daily)2const admin = require('firebase-admin');3const db = admin.firestore();45exports.cleanupActivityLog = async () => {6 const cutoff = new Date();7 cutoff.setDate(cutoff.getDate() - 90);89 let totalDeleted = 0;10 let query = db.collection('activity_log')11 .where('timestamp', '<', cutoff)12 .limit(500);1314 while (true) {15 const snap = await query.get();16 if (snap.empty) break;1718 const batch = db.batch();19 snap.docs.forEach(doc => batch.delete(doc.ref));20 await batch.commit();21 totalDeleted += snap.size;22 }2324 console.log(`Cleaned up ${totalDeleted} activity log entries`);25};Expected result: Activity log entries older than 90 days are automatically deleted daily, keeping the collection lean.
Complete working example
1FIRESTORE DATA MODEL:2 activity_log/{docId}3 userId: String4 userEmail: String (denormalized)5 action: "login" | "create" | "update" | "delete"6 resourceType: "post" | "order" | "profile" | "comment" | "settings"7 resourceId: String8 description: String9 metadata: Map (optional context)10 timestamp: Timestamp1112FIRESTORE INDEXES:13 activity_log: action + timestamp DESC14 activity_log: userId + timestamp DESC15 activity_log: resourceType + timestamp DESC1617LOGGING INTEGRATION:18 After Login:19 → logActivity('login', 'session', uid, 'User logged in')20 After Create Document:21 → logActivity('create', 'post', docId, 'Created: title')22 After Update Document:23 → logActivity('update', 'order', docId, 'Updated status to shipped')24 After Delete Document:25 → logActivity('delete', 'comment', docId, 'Deleted comment')2627ADMIN LOG VIEWER:28 Column29 ├── Row (filters)30 │ ├── DateTimePicker (start date)31 │ ├── DateTimePicker (end date)32 │ ├── DropDown (action type)33 │ ├── DropDown (resource type)34 │ └── TextField (user search)35 └── ListView (activity_log, filtered, paginated)36 └── Row37 ├── Icon (action type, colored)38 ├── Column39 │ ├── Text (userEmail)40 │ ├── Text (description)41 │ └── Text (resourceType badge)42 └── Text (timestamp)4344USER TIMELINE:45 ListView (activity_log, userId == me, limit 20)46 └── Row47 ├── Container (vertical line + dot)48 ├── Column49 │ ├── Text (description)50 │ └── Text (resourceType, small)51 └── Text (relative timestamp)5253DATA RETENTION:54 Scheduled Cloud Function (daily):55 → Delete activity_log where timestamp < 90 days ago56 → Batch delete in groups of 500Common mistakes when building a User Activity Log in FlutterFlow
Why it's a problem: Logging page_view events to Firestore on every navigation
How to avoid: Only log significant actions (login, create, update, delete) to Firestore. Track page views with a dedicated analytics service like Google Analytics or use batched summaries instead of individual events.
Why it's a problem: Not adding Firestore indexes for the activity_log filter combinations
How to avoid: Create composite indexes for every filter combination you support: action + timestamp, userId + timestamp, resourceType + timestamp. Firestore will prompt you with index creation links in error messages.
Why it's a problem: Not implementing data retention, letting the activity_log grow indefinitely
How to avoid: Implement a scheduled Cloud Function that deletes entries older than your retention period (e.g., 90 days). Run it daily to keep the collection at a manageable size.
Best practices
- Log only significant actions (create, update, delete, login) not passive browsing
- Denormalize userEmail on each log entry for efficient admin display
- Create Firestore composite indexes for all filter combinations used in the admin view
- Implement automatic data retention cleanup via scheduled Cloud Function
- Include human-readable descriptions on each log entry for quick scanning
- Use a reusable Custom Action for consistent logging across all Action Flows
- Add metadata for sensitive actions (what changed, old vs new values) for detailed auditing
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I want to build a user activity log in FlutterFlow. Show me the Firestore data model for an audit trail, a reusable Custom Action for logging activities, how to integrate it into Action Flows, an admin log viewer with date range and type filters, a user-facing activity timeline, and a scheduled Cloud Function for data retention cleanup.
Create an admin page with a row of filter dropdowns at the top (action type, resource type, date range) and a scrollable list below showing activity entries with icons, descriptions, and timestamps.
Frequently asked questions
How do I export activity logs for compliance reporting?
Create a Cloud Function that queries activity_log for a date range, formats the results as CSV or JSON, uploads to Firebase Storage, and returns a download URL. Trigger this function from an Export button on the admin log viewer.
Can I log which fields were changed in an update action?
Yes. In the metadata Map, include an object with the changed fields, old values, and new values. For example: metadata: {changes: {status: {old: 'pending', new: 'shipped'}}}. This creates a detailed audit trail for sensitive data.
Will the activity log slow down my app?
No. The logActivity Custom Action creates a single Firestore document, which takes milliseconds. It runs at the end of the Action Flow, so it does not block the primary action. The user experience is unaffected.
Can I set different retention periods for different action types?
Yes. Modify the cleanup Cloud Function to use different cutoff dates per action type. For example, delete 'login' entries after 30 days but keep 'delete' entries for 365 days for compliance purposes.
How do I search the activity log by description text?
Firestore does not support full-text search natively. For basic search, use prefix matching on the description field. For advanced search, integrate Algolia or use a Cloud Function that searches through logs and returns matching results.
Can RapidDev help build a compliance-grade audit system?
Yes. RapidDev can implement a full audit logging system with tamper-proof logs, field-level change tracking, compliance exports, role-based log access, alerting on suspicious activity, and integration with SIEM tools.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation