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.
- Getting Started
- Prerequisites
- Installation
- Supabase Configuration
- Supabase Setup
- Features
- Usage
- General Project Structure
- Documentation
- Contributing
- License
These instructions will help you set up and run the project on your local machine.
- Flutter SDK: Flutter installation guide
- Supabase account: Supabase
- Git: Git installation guide
-
Clone the repository:
git clone https://github.com/yourusername/flutter-supabase-orders.git cd flutter-supabase-orders
-
Install dependencies:
flutter pub get
-
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", ); }
- Create a new project in Supabase.
- Set up your tables and storage buckets as described in the Supabase Setup section.
- Get your Supabase URL and API key from the Supabase dashboard.
-
Create the
OrderStatus
enum:CREATE TYPE OrderStatus AS ENUM ( 'pending', 'completed', 'cancelled', 'noShow' );
-
Create the
OrderType
enum:CREATE TYPE OrderType AS ENUM ( 'direct', 'promo' );
-
Create the
PaymentMethod
enum:CREATE TYPE PaymentMethod AS ENUM ( 'visa', 'mastercard' );
-
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 );
-
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 );
-
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;
-
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;
-
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;
- Static implementation of Orders and Login using Flutter.
- Display status toast messages during login attempts.
- 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.
- Advanced authentication with multiple providers (email, Google, Apple, Facebook).
- Implement RLS policies for data security.
- Responsive design for different device sizes.
- State management implementation.
-
Run the application:
flutter run
-
Login using the email authentication.
-
View, filter, and paginate orders.
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
- 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.
- 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.
- Fork the repository.
- Create a new branch:
git checkout -b my-feature-branch
- Make your changes and commit them:
git commit -m 'Add some feature'
- Push to the branch:
git push origin my-feature-branch
- Submit a pull request.
This project is licensed under the MIT License - see the LICENSE file for details.