Build a dashboard where each user picks which widgets to display and in what order. Store widget layout config in a Firestore subcollection under the user document. Render the page dynamically using Generate Dynamic Children with a Conditional Builder that switches between Components like weather, calendar, tasks, and stats. A settings page lets users toggle visibility and drag-reorder widgets using a ReorderableListView Custom Widget.
Building a User-Configurable Dashboard in FlutterFlow
A fixed dashboard shows every user the same layout. A personalized dashboard lets each user choose which widgets appear and in what order, like a phone home screen. This tutorial stores widget config in Firestore, renders widgets dynamically, and provides a settings page for customization.
Prerequisites
- A FlutterFlow project with Firestore configured
- Separate Components created for each widget type (weather, tasks, calendar, stats)
- Understanding of Generate Dynamic Children and Conditional Builder
- FlutterFlow Pro plan for Custom Widget support
Step-by-step guide
Design the Firestore dashboard config schema
Design the Firestore dashboard config schema
Under each user document, create a dashboard_config subcollection or a single document with a widgetLayout field containing an ordered list of objects. Each object has: widgetType (String: weather, calendar, tasks, news, stats), isVisible (Boolean), and position (int). Seed a default config for new users via a Cloud Function on user creation. This way every user starts with a standard layout they can customize.
Expected result: Each user has a dashboard_config document in Firestore with an ordered list of widget entries.
Build individual widget Components for each dashboard section
Build individual widget Components for each dashboard section
Create separate FlutterFlow Components for each widget type. WeatherWidget: displays current temperature and condition from a weather API call. TasksWidget: shows the top 5 pending tasks from a tasks collection query. CalendarWidget: displays today's events in a compact list. StatsWidget: shows key metrics like total orders or active users. NewsWidget: lists the latest 3 articles. Each Component manages its own Backend Query and is self-contained.
Expected result: Five independent Components exist, each rendering its own data when placed on any page.
Render the dashboard dynamically with Generate Dynamic Children
Render the dashboard dynamically with Generate Dynamic Children
On the Dashboard page, add a Backend Query to load the user's dashboard_config. Add a Column widget and enable Generate Dynamic Children bound to the widgetLayout list filtered to isVisible == true and sorted by position. Inside the dynamic child, add a Conditional Builder checking widgetType: weather maps to WeatherWidget Component, calendar to CalendarWidget, tasks to TasksWidget, and so on. Each branch renders the matching Component.
Expected result: The dashboard page renders only the widgets the user has enabled, in their chosen order.
Create a settings page with visibility toggles
Create a settings page with visibility toggles
Add a DashboardSettings page. Load the same dashboard_config. Display a ListView of all available widgets, each row showing: the widget name, a preview icon, and a Switch toggled to the isVisible value. When a Switch changes, update the corresponding entry in the widgetLayout list and save back to Firestore. Changes take effect immediately when the user returns to the dashboard.
Expected result: Users can toggle widgets on or off and the dashboard updates to reflect their choices.
Add drag-to-reorder functionality with a Custom Widget
Add drag-to-reorder functionality with a Custom Widget
Create a Custom Widget wrapping Flutter's ReorderableListView. The widget accepts the widgetLayout list as a parameter and renders each entry as a draggable row with a drag handle icon on the right. On reorder, the widget calls the onReorder callback which updates the position values in the list and triggers an Action Parameter callback to the parent page. The parent saves the new order to Firestore. Use ValueKey per widget type so Flutter only re-renders moved items.
1// Custom Widget: ReorderableDashboardList2import 'package:flutter/material.dart';34class ReorderableDashboardList extends StatefulWidget {5 final List<Map<String, dynamic>> widgets;6 final void Function(List<Map<String, dynamic>> reordered) onReorder;78 const ReorderableDashboardList({9 required this.widgets,10 required this.onReorder,11 });1213 @override14 State<ReorderableDashboardList> createState() =>15 _ReorderableDashboardListState();16}1718class _ReorderableDashboardListState19 extends State<ReorderableDashboardList> {20 late List<Map<String, dynamic>> _items;2122 @override23 void initState() {24 super.initState();25 _items = List.from(widget.widgets);26 }2728 @override29 Widget build(BuildContext context) {30 return ReorderableListView.builder(31 itemCount: _items.length,32 onReorder: (oldIndex, newIndex) {33 setState(() {34 if (newIndex > oldIndex) newIndex--;35 final item = _items.removeAt(oldIndex);36 _items.insert(newIndex, item);37 for (int i = 0; i < _items.length; i++) {38 _items[i]['position'] = i;39 }40 });41 widget.onReorder(_items);42 },43 itemBuilder: (context, index) {44 final item = _items[index];45 return ListTile(46 key: ValueKey(item['widgetType']),47 leading: Icon(Icons.dashboard),48 title: Text(item['widgetType']),49 trailing: Icon(Icons.drag_handle),50 );51 },52 );53 }54}Expected result: Users drag widgets up or down to reorder them and the new positions persist to Firestore.
Handle new users with default dashboard config
Handle new users with default dashboard config
Write a Cloud Function that triggers on new user creation. The function creates a dashboard_config document with a default widgetLayout: weather at position 0, stats at position 1, tasks at position 2, calendar at position 3, news at position 4, all visible. On the Dashboard page, if the config query returns empty, show a loading spinner while the Cloud Function provisions the default. This ensures every user sees a populated dashboard on first login.
Expected result: New users see a pre-configured dashboard immediately after sign-up without needing to configure anything.
Complete working example
1FIRESTORE DATA MODEL:2 users/{uid}/dashboard_config (single doc)3 widgetLayout: [4 { widgetType: 'weather', isVisible: true, position: 0 },5 { widgetType: 'stats', isVisible: true, position: 1 },6 { widgetType: 'tasks', isVisible: true, position: 2 },7 { widgetType: 'calendar', isVisible: true, position: 3 },8 { widgetType: 'news', isVisible: false, position: 4 }9 ]1011PAGE STATE (Dashboard):12 dashboardConfig: JSON (loaded from Firestore)1314WIDGET TREE — Dashboard Page:15 Scaffold16 AppBar (title: 'My Dashboard', action: gear icon → Navigate to Settings)17 Body: Column18 ├── Backend Query → dashboard_config doc19 ├── Column (Generate Dynamic Children → widgetLayout filtered isVisible)20 │ └── Per entry:21 │ Conditional Builder (widgetType)22 │ 'weather' → WeatherWidget Component23 │ 'stats' → StatsWidget Component24 │ 'tasks' → TasksWidget Component25 │ 'calendar' → CalendarWidget Component26 │ 'news' → NewsWidget Component27 └── SizedBox (80, bottom padding)2829WIDGET TREE — Settings Page:30 Scaffold31 AppBar (title: 'Customize Dashboard')32 Body: Column33 ├── Text ('Toggle widgets and drag to reorder')34 ├── SizedBox (16)35 ├── Custom Widget: ReorderableDashboardList36 │ Each row: Row37 │ ├── Icon (widget type icon)38 │ ├── Text (widget name)39 │ ├── Switch (isVisible toggle)40 │ └── Icon (drag_handle)41 └── Button 'Save Layout' → Update Firestore docCommon mistakes when building a Personalized Dashboard With Widgets in FlutterFlow
Why it's a problem: Re-rendering all widget Components when one widget is reordered
How to avoid: Assign a ValueKey based on widgetType to each dynamic child so Flutter only moves the DOM node instead of rebuilding it.
Why it's a problem: Saving reorder changes synchronously before updating the UI
How to avoid: Update the Page State list immediately for instant UI response, then fire the Firestore update asynchronously in the background.
Why it's a problem: Storing dashboard config as multiple Firestore documents per widget
How to avoid: Store the entire widgetLayout as an array field on a single dashboard_config document for a single read.
Best practices
- Store the full widget layout in a single Firestore document to minimize reads
- Use Conditional Builder with Component references for clean widget switching
- Assign ValueKey to dynamic children to prevent unnecessary rebuilds on reorder
- Provision a default dashboard config for new users via Cloud Function
- Update UI state immediately on reorder and save to Firestore asynchronously
- Keep each dashboard widget as an independent Component with its own data source
- Add a Reset to Default button on the settings page for users who want to start over
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I want to build a personalized dashboard in FlutterFlow where each user can toggle which widgets are visible and drag-reorder them. Widget config is stored in Firestore. The dashboard renders Components dynamically using Generate Dynamic Children and Conditional Builder. Show me the data model, widget tree, and a ReorderableListView Custom Widget.
Create a dashboard page with a column of placeholder cards for weather, tasks, calendar, stats, and news sections. Add a settings gear icon in the app bar.
Frequently asked questions
Can users add the same widget type twice?
By default the system allows one instance per widget type. To support duplicates, use a unique widgetId (UUID) instead of widgetType as the identifier and allow multiple entries of the same type in the layout array.
How do I add new widget types without breaking existing configs?
When you add a new widget type, update the Cloud Function that provisions defaults. For existing users, check if the new type exists in their config on page load. If missing, append it with isVisible: false so it appears in settings but does not change their current dashboard.
Can I make widgets resizable like a grid layout?
FlutterFlow does not support drag-resizable grid layouts natively. You can approximate it with predefined size options (small, medium, large) stored in the config and rendered as different Component variants.
How do I prevent the dashboard from flickering on load?
Cache the last known config in App State (persisted). On page load, render from the cached config immediately while the Backend Query fetches the latest version from Firestore.
Does the drag-to-reorder work on both mobile and web?
Yes. ReorderableListView supports touch drag on mobile and mouse drag on web. On mobile, a long press activates the drag. On web, clicking and dragging the handle works immediately.
Can RapidDev build a custom dashboard system for my app?
Yes. RapidDev can build fully configurable dashboards with drag-reorder, resizable widgets, role-based default layouts, and real-time data integrations.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation