# How to Build a Personalized Dashboard With Widgets in FlutterFlow

- Tool: FlutterFlow
- Difficulty: Beginner
- Time required: 25-30 min
- Compatibility: FlutterFlow Pro+ (Custom Widget required for reorder)
- Last updated: March 2026

## TL;DR

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.

## Before you start

- 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

### 1. 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.

### 2. 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.

### 3. 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.

### 4. 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.

### 5. 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.

```
// Custom Widget: ReorderableDashboardList
import 'package:flutter/material.dart';

class ReorderableDashboardList extends StatefulWidget {
  final List<Map<String, dynamic>> widgets;
  final void Function(List<Map<String, dynamic>> reordered) onReorder;

  const ReorderableDashboardList({
    required this.widgets,
    required this.onReorder,
  });

  @override
  State<ReorderableDashboardList> createState() =>
      _ReorderableDashboardListState();
}

class _ReorderableDashboardListState
    extends State<ReorderableDashboardList> {
  late List<Map<String, dynamic>> _items;

  @override
  void initState() {
    super.initState();
    _items = List.from(widget.widgets);
  }

  @override
  Widget build(BuildContext context) {
    return ReorderableListView.builder(
      itemCount: _items.length,
      onReorder: (oldIndex, newIndex) {
        setState(() {
          if (newIndex > oldIndex) newIndex--;
          final item = _items.removeAt(oldIndex);
          _items.insert(newIndex, item);
          for (int i = 0; i < _items.length; i++) {
            _items[i]['position'] = i;
          }
        });
        widget.onReorder(_items);
      },
      itemBuilder: (context, index) {
        final item = _items[index];
        return ListTile(
          key: ValueKey(item['widgetType']),
          leading: Icon(Icons.dashboard),
          title: Text(item['widgetType']),
          trailing: Icon(Icons.drag_handle),
        );
      },
    );
  }
}
```

**Expected result:** Users drag widgets up or down to reorder them and the new positions persist to Firestore.

### 6. 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 code example

File: `FlutterFlow Personalized Dashboard Setup`

```text
FIRESTORE DATA MODEL:
  users/{uid}/dashboard_config (single doc)
    widgetLayout: [
      { widgetType: 'weather', isVisible: true, position: 0 },
      { widgetType: 'stats', isVisible: true, position: 1 },
      { widgetType: 'tasks', isVisible: true, position: 2 },
      { widgetType: 'calendar', isVisible: true, position: 3 },
      { widgetType: 'news', isVisible: false, position: 4 }
    ]

PAGE STATE (Dashboard):
  dashboardConfig: JSON (loaded from Firestore)

WIDGET TREE — Dashboard Page:
  Scaffold
    AppBar (title: 'My Dashboard', action: gear icon → Navigate to Settings)
    Body: Column
      ├── Backend Query → dashboard_config doc
      ├── Column (Generate Dynamic Children → widgetLayout filtered isVisible)
      │     └── Per entry:
      │           Conditional Builder (widgetType)
      │             'weather' → WeatherWidget Component
      │             'stats' → StatsWidget Component
      │             'tasks' → TasksWidget Component
      │             'calendar' → CalendarWidget Component
      │             'news' → NewsWidget Component
      └── SizedBox (80, bottom padding)

WIDGET TREE — Settings Page:
  Scaffold
    AppBar (title: 'Customize Dashboard')
    Body: Column
      ├── Text ('Toggle widgets and drag to reorder')
      ├── SizedBox (16)
      ├── Custom Widget: ReorderableDashboardList
      │     Each row: Row
      │       ├── Icon (widget type icon)
      │       ├── Text (widget name)
      │       ├── Switch (isVisible toggle)
      │       └── Icon (drag_handle)
      └── Button 'Save Layout' → Update Firestore doc
```

## Common mistakes

- **Re-rendering all widget Components when one widget is reordered** — Without ValueKey, Flutter destroys and rebuilds every child in the list on reorder, causing all Backend Queries to re-fire and the UI to flicker. Fix: Assign a ValueKey based on widgetType to each dynamic child so Flutter only moves the DOM node instead of rebuilding it.
- **Saving reorder changes synchronously before updating the UI** — Waiting for the Firestore write to complete before updating the visual order creates a noticeable lag on every drag operation. Fix: Update the Page State list immediately for instant UI response, then fire the Firestore update asynchronously in the background.
- **Storing dashboard config as multiple Firestore documents per widget** — Loading the dashboard requires reading one document per widget type, multiplying read costs. For five widgets that is five reads instead of one. Fix: 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

## 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.

---

Source: https://www.rapidevelopers.com/flutterflow-tutorials/how-to-build-a-personalized-dashboard-with-widgets-in-flutterflow
© RapidDev — https://www.rapidevelopers.com/flutterflow-tutorials/how-to-build-a-personalized-dashboard-with-widgets-in-flutterflow
