Skip to content

DanielAndresClavijo/orders-app

Repository files navigation

Flutter Supabase Orders App

This project is a Flutter application that integrates with Supabase to manage and display orders. The app includes functionalities for user authentication, order filtering, pagination, and image management. It is built to demonstrate various levels of implementation from static data to full backend integration with Supabase.

Table of Contents

Getting Started

These instructions will help you set up and run the project on your local machine.

Prerequisites

Installation

  1. Clone the repository:

    git clone https://github.com/yourusername/flutter-supabase-orders.git
    cd flutter-supabase-orders
  2. Install dependencies:

    flutter pub get
  3. Set up environment variables: Set your Supabase credentials to lib/config/app_config.dart

    factory AppConfig.initialize() {
      return const AppConfig._(
        clientEnvironment: ClientEnvironment.fromBaaS,
        supabaseUrl: "https://your-supabase-url.supabase.co",
        supabaseKey: "your-supabase-api-key",
        orderTableName: "orders",
      );
    }

Supabase Configuration

  1. Create a new project in Supabase.
  2. Set up your tables and storage buckets as described in the Supabase Setup section.
  3. Get your Supabase URL and API key from the Supabase dashboard.

Supabase Setup

  1. Create the OrderStatus enum:

    CREATE TYPE OrderStatus AS ENUM (
      'pending',
      'completed',
      'cancelled',
      'noShow'
    );
  2. Create the OrderType enum:

    CREATE TYPE OrderType AS ENUM (
      'direct',
      'promo'
    );
  3. Create the PaymentMethod enum:

    CREATE TYPE PaymentMethod AS ENUM (
      'visa',
      'mastercard'
    );
  4. Create the orders table:

    CREATE TABLE orders (
      id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
      created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
      updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
      company_name TEXT,
      address TEXT,
      user_id UUID REFERENCES auth.users(id),
      shipment FLOAT,
      tip FLOAT,
      status OrderStatus DEFAULT 'pending',
      type OrderType DEFAULT 'direct',
      payment_method PaymentMethod,
      image_id TEXT
    );
  5. Create the product_order table:

    CREATE TABLE product_order (
      id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
      created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
      name TEXT,
      price FLOAT,
      discount FLOAT,
      order_id BIGINT REFERENCES orders(id),
      count INT
    );
  6. Set up Row Level Security (RLS) policies:

    ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
    ALTER TABLE product_order ENABLE ROW LEVEL SECURITY;
    
    CREATE POLICY "user_orders_policy"
    ON orders
    FOR SELECT
    USING (auth.uid() = user_id);
    
    CREATE POLICY "user_product_order_policy"
    ON product_order
    FOR SELECT
    USING (EXISTS (
      SELECT 1
      FROM orders
      WHERE orders.id = product_order.order_id
      AND orders.user_id = auth.uid()
    ));
    
    ALTER TABLE orders FORCE ROW LEVEL SECURITY;
    ALTER TABLE product_order FORCE ROW LEVEL SECURITY;
  7. Create the function get_orders_with_products:

    CREATE OR REPLACE FUNCTION get_orders_with_products (
      p_order_id BIGINT DEFAULT NULL,
      p_status TEXT DEFAULT NULL,
      p_limit INT DEFAULT 10,
      p_offset INT DEFAULT 0
    ) RETURNS TABLE (
      total_count BIGINT,
      id BIGINT,
      created_at TIMESTAMPTZ,
      updated_at TIMESTAMPTZ,
      company_name TEXT,
      address TEXT,
      user_id UUID,
      shipment FLOAT,
      tip FLOAT,
      status TEXT,
      type TEXT,
      payment_method TEXT,
      image_id TEXT,
      products JSON
    ) AS $$
    BEGIN
      RETURN QUERY WITH 
        filtered_orders AS ( 
          SELECT * FROM orders
          WHERE (p_order_id IS NULL OR orders.id = p_order_id)
          AND (p_status IS NULL OR orders.status::text = p_status)
          ORDER BY orders.id
        ),
        total_count_cte AS (
          SELECT COUNT(*) AS total_count FROM filtered_orders
        ),
        paginated_orders AS (
          SELECT * FROM filtered_orders LIMIT p_limit OFFSET p_offset
        )  
        SELECT 
          (SELECT total_count_cte.total_count FROM total_count_cte) AS total_count,
          paginated_orders.id,
          paginated_orders.created_at, 
          paginated_orders.updated_at, 
          paginated_orders.company_name, 
          paginated_orders.address, 
          paginated_orders.user_id, 
          paginated_orders.shipment, 
          paginated_orders.tip, 
          paginated_orders.status::text, 
          paginated_orders.type::text, 
          paginated_orders.payment_method::text, 
          paginated_orders.image_id,
          COALESCE(
            json_agg(
              json_build_object(
                'id', product_order.id,
                'created_at', product_order.created_at,
                'name', product_order.name,
                'price', product_order.price,
                'discount', product_order.discount,
                'count', product_order.count,
                'order_id', product_order.order_id
              )
            ) FILTER (WHERE product_order.id IS NOT NULL), '[]' 
          ) AS products 
        FROM paginated_orders
        LEFT JOIN product_order 
          ON paginated_orders.id = product_order.order_id
        GROUP BY 
          paginated_orders.id,
          paginated_orders.created_at, 
          paginated_orders.updated_at, 
          paginated_orders.company_name, 
          paginated_orders.address, 
          paginated_orders.user_id, 
          paginated_orders.shipment, 
          paginated_orders.tip, 
          paginated_orders.status::text, 
          paginated_orders.type::text, 
          paginated_orders.payment_method::text, 
          paginated_orders.image_id;
    END;
    $$ LANGUAGE plpgsql;
  8. Create the function product_order_check_user_permission:

    CREATE OR REPLACE FUNCTION product_order_check_user_permission()
    RETURNS BOOLEAN AS $$
    DECLARE
        order_user_id UUID;
    BEGIN
        SELECT o.user_id
        INTO order_user_id
        FROM orders o
        JOIN product_order po ON o.id = po.order_id
        WHERE po.id = $1;
    
        RETURN order_user_id = current_setting('jwt.claims.sub', true)::uuid;
    END;
    $$ LANGUAGE plpgsql;

Features

Level 1

  • Static implementation of Orders and Login using Flutter.
  • Display status toast messages during login attempts.

Level 2

  • Integration with Supabase for data fetching.
  • Authentication using Supabase with email.
  • Store and manage images in Supabase storage.
  • Implement order filtering, status filtering, and pagination.

Level 3

  • Advanced authentication with multiple providers (email, Google, Apple, Facebook).
  • Implement RLS policies for data security.
  • Responsive design for different device sizes.
  • State management implementation.

Usage

  1. Run the application:

    flutter run
  2. Login using the email authentication.

  3. View, filter, and paginate orders.

General project structure

flutter-supabase-orders/
├── android/
├── ios/
├── lib/
│   ├── main.dart
│   ├── config/
│   │   ├── app_config.dart
│   │   ├── assets.dart
│   │   ├── navigation_history.dart
│   │   ├── theme.dart
│   │   ├── router/
│   │   │   ├── auth/
│   │   │   ├── orders/
│   │   │   ├── welcome/
│   │   │   ├── common_router.dart
│   ├── data/
│   │   ├── datasources/
│   │   │   ├── local/
│   │   │   ├── supabase_impl/
│   │   ├── repositories_impl/
│   │   │   ├── auth_repository_impl.dart
│   │   │   ├── orders_repository_impl.dart
│   ├── domain/
│   │   ├── entities/
│   │   ├── repositories/
│   │   ├── usecases/
│   ├── injector/
│   │   ├── injector.dart
│   │   ├── injector_provider.dart
│   ├── ui/
│   │   ├── core/
│   │   ├── features/
│   │   │   ├── auth/
│   │   │   ├── orders/
│   │   │   ├── welcome/
├── pubspec.yaml
└── README.md

Documentation

Supabase Integration

  • OrderStatus Enum: Defines possible statuses for orders (pending, completed, cancelled, noShow).
  • OrderType Enum: Defines possible types for orders (direct, promo).
  • PaymentMethod Enum: Defines possible payment methods for orders (visa, mastercard, paypal).
  • Orders Table: Contains order details including status, type, payment method, and associated image ID.
  • Product Order Table: Stores product details linked to orders.
  • Images Table: Stores image URLs and links to user IDs.
  • RLS Policies: Ensure that users can only access their own data.
  • Functions:
    • get_orders_with_products: Fetches orders with related products and supports pagination and filtering.
    • product_order_check_user_permission: Ensures only authorized users can access product orders.

Flutter Implementation

  • Splash Screen: Initial loading screen.
  • Login Screen: Uses Supabase authentication to log in users.
  • Home Screen: Main screen after login.
  • Orders Screen: Fetches and displays orders with options to filter by status and paginate.
  • Order Detail Screen: Displays detailed information about a specific order.
  • Side Drawer Menu: Contains user information, filtering options, pagination controls, and a logout button.

Contributing

  1. Fork the repository.
  2. Create a new branch: git checkout -b my-feature-branch
  3. Make your changes and commit them: git commit -m 'Add some feature'
  4. Push to the branch: git push origin my-feature-branch
  5. Submit a pull request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

Food ordering app

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published