← Back to list

FlutterFlow Micro-SaaS: Overcoming Visual Limitations with Custom Code (Day 4)

How to use Custom Actions, Functions, and Custom Widgets to build advanced business logic for a Multi-Tenant Salon SaaS.

Milan Marjanovic · 2026-05-25 11:00 · 4 claps · 3.3 min read
#flutterflow #no-code #saas #database #web-development
Open on Medium ↗
Wiki topics: 🌐 · Web Development 📱 · Mobile Development

FlutterFlow Micro-SaaS: Overcoming Visual Limitations with Custom Code (Day 4)

How to use Custom Actions, Functions, and Custom Widgets to build advanced business logic for a Multi-Tenant Salon SaaS.

While FlutterFlow provides an incredible visual builder for 90% of your application, a real-world B2B Micro-SaaS will always require complex business logic that standard visual actions simply cannot handle.

Whether you need to calculate real-time slot availability, hash passwords, or manipulate complex JSON data from third-party APIs, knowing how to write custom Dart code is what separates a basic hobbyist app from a production-ready SaaS.

Today, we are diving deep into how to write and integrate Custom Functions, Custom Actions, and Custom Widgets inside our Salon Management SaaS to bypass visual limitations.

  1. Custom Functions vs. Custom Actions: What’s the Difference?

Before writing code, you must understand when to use which in FlutterFlow. Choosing the wrong one can break your app architecture.

  • Custom Functions (Pure Dart): These are used for data transformation. They take inputs, process them instantly, and return a value immediately. They cannot trigger screen changes or make asynchronous database calls. (e.g., Calculating a discount percentage).
  • Custom Actions (Asynchronous Dart/Flutter): These are used for execution flows. They can perform background tasks, talk to APIs, update databases, and run asynchronous (async/await) code. (e.g., Running a complex scheduling loop before blocking a calendar slot).
  1. Practical SaaS Example: The Appointment Slot Calculator

In our Salon SaaS, a user wants to book an appointment. We cannot just let them pick any time; we need a script that checks the salon’s working hours, looks at existing bookings, and returns a list of only available time slots.

Doing this visually with FlutterFlow action blocks is a nightmare. Doing it with a Custom Function is clean and fast.

The Logic (Dart Code):

Here is how we write a Custom Function that takes the businessStartTime, businessEndTime, serviceDuration (e.g., 45 mins), and a list of existingBookings to generate free slots.

dart

import 'dart:math' as math;
List<DateTime> generateAvailableSlots(
  DateTime selectedDate,
  DateTime startTime,
  DateTime endTime,
  int serviceDurationMinutes,
  List<DateTime> bookedSlots,
) {
  List<DateTime> availableSlots = [];

  // Set the loop to start at the business opening time on the selected day
  DateTime currentSlot = DateTime(
    selectedDate.year,
    selectedDate.month,
    selectedDate.day,
    startTime.hour,
    startTime.minute,
  );

  DateTime endLimit = DateTime(
    selectedDate.year,
    selectedDate.month,
    selectedDate.day,
    endTime.hour,
    endTime.minute,
  );
  while (currentSlot.isBefore(endLimit)) {
    // Check if the current slot overlaps with any existing bookings
    bool isBooked = bookedSlots.any((booking) => 
      booking.year == currentSlot.year &&
      booking.month == currentSlot.month &&
      booking.day == currentSlot.day &&
      booking.hour == currentSlot.hour &&
      booking.minute == currentSlot.minute
    );
    if (!isBooked) {
      availableSlots.add(currentSlot);
    }

    // Move to the next available interval based on service duration
    currentSlot = currentSlot.add(Duration(minutes: serviceDurationMinutes));
  }
  return availableSlots;
}

Use code with caution.

How to set this up in FlutterFlow:

  1. Go to the Custom Code tab on the left menu.

  2. Click Add -> Function.

  3. Name it generateAvailableSlots.

  4. Define the Return Type as List <DateTime>.

  5. Add your Arguments (Inputs): selectedDate (DateTime), startTime (DateTime), endTime (DateTime), serviceDurationMinutes (Integer), and bookedSlots (List ).

  6. Paste the Dart code, click Save, and run the Check Code (Compile) command to ensure zero syntax errors.

  7. When Visual Actions Fail: Batch Updating via Custom Actions

Imagine a salon owner wants to block out an entire week for a vacation. Modifying 50 individual calendar slots one by one visually via the Firestore Update action is slow and causes bad user experience.

Instead, we create a Custom Action called batchBlockCalendarSlots that runs a heavy Firestore write operations in the background.

dart

import 'package:cloud_firestore/cloud_firestore.dart';
Future batchBlockCalendarSlots(
  String tenantId,
  DateTime startDate,
  DateTime endDate,
  String reason,
) async {
  final firestore = FirebaseFirestore.instance;
  final batch = firestore.batch();

  DateTime currentDate = startDate;

  // Loop through days between start and end date
  while (currentDate.isBefore(endDate) || currentDate.isAtSameMomentAs(endDate)) {
    DocumentReference blockRef = firestore
        .collection('tenants')
        .doc(tenantId)
        .collection('blocked_days')
        .doc(); // Generates an automatic ID

    batch.set(blockRef, {
      'date': currentDate,
      'reason': reason,
      'createdAt': FieldValue.serverTimestamp(),
    });

    currentDate = currentDate.add(Duration(days: 1));
  }

  // Commit all database writes simultaneously for maximum efficiency
  await batch.commit();
}

Use code with caution.

  1. Key Lessons Learned When Writing Custom Logic
  2. Never do heavy math in the UI: If you need to calculate taxes, earnings, or split percentages for salon staff, do it inside a Custom Function. Running complex algorithms directly in UI variables will cause lag on older mobile devices.
  3. Use AI tools defensively: FlutterFlow has an built-in AI Copilot for code. It is great for basic boilerplate code, but always verify its imports. The biggest reason custom code fails to compile in FlutterFlow is missing or duplicate package imports at the top of the file.
  4. Handle null values explicitly: FlutterFlow app states can sometimes return null values. Always add conditional safety checks in your Dart code (e.g., if (tenantId == null) return;) to protect your app from crashing in production.

◀️ Missed the previous lessons?

Tomorrow, for Day 5, we will move into one of the most exciting phases: Setting up API Integrations and Stripe Connect for Automated SaaS Payouts. Make sure to follow so you don’t miss it!


메타데이터
post_id
9fbf90445da2
slug
flutterflow-micro-saas-overcoming-visual-limitations-with-custom-code-day-4-9fbf90445da2
url
https://medium.com/@milan_m/flutterflow-micro-saas-overcoming-visual-limitations-with-custom-code-day-4-9fbf90445da2
canonical_url
https://medium.com/@milan_m/flutterflow-micro-saas-overcoming-visual-limitations-with-custom-code-day-4-9fbf90445da2
author_url
https://medium.com/@milan_m
status
ok
fetched_at
2026-06-09 15:37:30