diff --git a/demos/supabase-trello/.env.template b/demos/supabase-trello/.env.template
new file mode 100644
index 00000000..073dd3bb
--- /dev/null
+++ b/demos/supabase-trello/.env.template
@@ -0,0 +1,4 @@
+# Update these with your own values
+SUPABASE_URL=https://aaaaaaaaaaaa.supabase.co
+SUPABASE_ANON_KEY=my_anon_key
+POWERSYNC_URL=https://aaaaaaaaaaaa.powersync.journeyapps.com
diff --git a/demos/supabase-trello/.gitignore b/demos/supabase-trello/.gitignore
new file mode 100644
index 00000000..7f4b32b9
--- /dev/null
+++ b/demos/supabase-trello/.gitignore
@@ -0,0 +1,47 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+.env
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Web related
+lib/generated_plugin_registrant.dart
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
diff --git a/demos/supabase-trello/.metadata b/demos/supabase-trello/.metadata
new file mode 100644
index 00000000..2e239dba
--- /dev/null
+++ b/demos/supabase-trello/.metadata
@@ -0,0 +1,45 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled.
+
+version:
+ revision: d3d8effc686d73e0114d71abdcccef63fa1f25d2
+ channel: stable
+
+project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+ platforms:
+ - platform: root
+ create_revision: d3d8effc686d73e0114d71abdcccef63fa1f25d2
+ base_revision: d3d8effc686d73e0114d71abdcccef63fa1f25d2
+ - platform: android
+ create_revision: d3d8effc686d73e0114d71abdcccef63fa1f25d2
+ base_revision: d3d8effc686d73e0114d71abdcccef63fa1f25d2
+ - platform: ios
+ create_revision: d3d8effc686d73e0114d71abdcccef63fa1f25d2
+ base_revision: d3d8effc686d73e0114d71abdcccef63fa1f25d2
+ - platform: linux
+ create_revision: d3d8effc686d73e0114d71abdcccef63fa1f25d2
+ base_revision: d3d8effc686d73e0114d71abdcccef63fa1f25d2
+ - platform: macos
+ create_revision: d3d8effc686d73e0114d71abdcccef63fa1f25d2
+ base_revision: d3d8effc686d73e0114d71abdcccef63fa1f25d2
+ - platform: web
+ create_revision: d3d8effc686d73e0114d71abdcccef63fa1f25d2
+ base_revision: d3d8effc686d73e0114d71abdcccef63fa1f25d2
+ - platform: windows
+ create_revision: d3d8effc686d73e0114d71abdcccef63fa1f25d2
+ base_revision: d3d8effc686d73e0114d71abdcccef63fa1f25d2
+
+ # User provided section
+
+ # List of Local paths (relative to this file) that should be
+ # ignored by the migrate tool.
+ #
+ # Files that are not part of the templates will be ignored by default.
+ unmanaged_files:
+ - 'lib/main.dart'
+ - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/demos/supabase-trello/LICENSE b/demos/supabase-trello/LICENSE
new file mode 100644
index 00000000..0e259d42
--- /dev/null
+++ b/demos/supabase-trello/LICENSE
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+ HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display,
+ communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+ likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+ v. rights protecting the extraction, dissemination, use and reuse of data
+ in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation
+ thereof, including any amended or successor version of such
+ directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+ world based on applicable law or treaty, and any national
+ implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+ warranties of any kind concerning the Work, express, implied,
+ statutory or otherwise, including without limitation warranties of
+ title, merchantability, fitness for a particular purpose, non
+ infringement, or the absence of latent or other defects, accuracy, or
+ the present or absence of errors, whether or not discoverable, all to
+ the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without
+ limitation any person's Copyright and Related Rights in the Work.
+ Further, Affirmer disclaims responsibility for obtaining any necessary
+ consents, permissions or other rights required for any use of the
+ Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to
+ this CC0 or use of the Work.
diff --git a/demos/supabase-trello/README.md b/demos/supabase-trello/README.md
new file mode 100644
index 00000000..a267c88e
--- /dev/null
+++ b/demos/supabase-trello/README.md
@@ -0,0 +1,342 @@
+# Trello Clone Using PowerSync + Flutter
+
+## Introduction
+
+Trello clone app built with [Flutter](https://flutter.dev/), [PowerSync](https://powersync.co/) and [Supabase](https://supabase.io/)
+
+
+
+# Running the app
+
+Ensure you have [melos](https://melos.invertase.dev/~melos-latest/getting-started) installed.
+
+1. `cd demos/supabase-trello`
+2. `melos prepare`
+3. `cp .env.template .env`
+4. Insert your Supabase and PowerSync project credentials into `.env` (See instructions below)
+5. `flutter run`
+
+# Getting Started
+
+First check out [the integration guide](https://docs.powersync.co/integration-guides/supabase-+-powersync) for [PowerSync](https://powersync.co/) and [Supabase](https://supabase.io/).
+
+Before you proceed, we assume that you have already signed up for free accounts with both Supabase and PowerSync. If you haven't signed up for a **PowerSync account** yet, [click here](https://accounts.journeyapps.com/portal/free-trial?powersync=true) (and if you haven't signed up for **Supabase** yet, [click here](https://supabase.com/dashboard/sign-up)). We also assume that you already have Flutter set up.
+
+Next up we will follow these steps:
+
+1. Configure Supabase:
+ - Create the database schema
+ - Create the Postgres publication
+2. Configure PowerSync:
+ - Create connection to Supabase
+ - Configure global Sync Rules
+4. Configure the Trello clone app
+5. Run the app and test
+6. Configure improved Sync Rules
+7. Run the app and test
+
+## Configure Supabase
+
+- Create a new Supabase project (or use an existing project if you prefer) and follow the below steps.
+- For ease of use of this Flutter app, you can **disable email confirmation** in your Supabase Auth settings. In your Supabase project, go to _"Authentication"_ --> _"Providers"_ -> _"Email"_ and then disable _"Confirm email"_. If you keep email confirmation enabled, the Supabase user confirmation email will reference the default Supabase Site URL of http://localhost:3000 — you can ignore this.
+
+### Create the Database Schema
+After creating the Supabase project, we still need to create the tables in the database. For this application we need the following tables:
+
+* `activity`
+* `attachment`
+* `board`
+* `card`
+* `checklist`
+* `comment`
+* `listboard`
+* `member`
+* `models`
+* `trellouser`
+* `workspace`
+* `board_label`
+* `card_label`
+
+_(We give a brief overview of the app domain model later in this README.)_
+
+Do the following:
+- Open the `tables.sql` file, and copy the contents.
+- Paste this into the *Supabase SQL Editor*
+- Run the SQL statements in the Supabase SQL Editor. (If you get a warning about a "potentially destructive operation", that's a false positive that you can safely ignore.)
+
+### Create the Postgres Publication
+
+PowerSync uses the Postgres [Write Ahead Log (WAL)](https://www.postgresql.org/docs/current/wal-intro.html) to replicate data changes in order to keep PowerSync SDK clients up to date. To enable this we need to create a `publication` in Supabase.
+
+Run the below SQL statement in your Supabase SQL Editor:
+```sql
+create publication powersync for table activity, attachment, board, card, checklist, comment, listboard, member, trellouser, workspace, board_label, card_label;
+```
+
+## Configuring PowerSync
+
+We need to connect PowerSync to the Supabase Postgres database:
+
+- In the [PowerSync dashboard](https://powersync.journeyapps.com/) project tree, click on _"Create new instance"_.
+
+- Give your instance a name, such as _"Trello Clone"_.
+
+- In the _"Edit Instance"_ dialog, navigate to the _"Credentials"_ tab and enable _"Use Supabase Auth"_.
+
+- Under the _"Connections"_ tab, click on the + icon.
+
+- On the subsequent screen, we'll configure the connection to Supabase. This is simplest using your Supabase database URI. In your Supabase dashboard, navigate to _"Project Settings"_ -> _"Database"_. Then, under the _"Connection String"_ section, switch to URI and copy the value.
+
+- Paste the copied value into the _"URI"_ field in PowerSync.
+
+- Enter the Password for the `postgres` user in your Supabase database. (Supabase also [refers to this password](https://supabase.com/docs/guides/database/managing-passwords) as the database password or project password)
+
+- Click _"Test Connection"_ and fix any errors.
+
+- Click "Save"
+
+PowerSync deploys and configures an isolated cloud environment for you, which will take a few minutes to complete.
+
+
+## Configuring Sync Rules - 1
+
+PowerSync [Sync Rules](https://docs.powersync.co/usage/sync-rules) allow developers to control which data gets synced to which user devices using a SQL-like syntax in a YAML file. For this Trello clone demo app, we're first going to use naive global sync rules, and then present improved rules that take the domain permissions into account.
+
+
+### Global Sync Rules to Get Things Working
+
+We can be naive about it, and start by using a global bucket definition that at least specifies in some way which users can get data.
+
+- Copy the contents of `sync-rules-0.yaml` to `sync-rules.yaml` under your PowerSync project instance.
+- In the top right of the `sync-rules.yaml` editor, click _"Deploy sync rules"_.
+- Confirm in the dialog and wait a couple of minutes for the deployment to complete.
+
+When you now run the app (after completing the next step to configure and run the app), it will actually show and retain data. The app code itself applies some basic filtering to only show data that belongs to the current user, or according to the visibility and membership settings of the various workspaces and boards.
+
+
+## Configuring Flutter App
+
+We need to configure the app to use the correct PowerSync and Supabase projects.
+
+- Copy the `trelloappclone_flutter/.env.template` file to `trelloappclone_flutter/.env`.
+- Replace the values for `SUPABASE_URL` and `SUPABASE_ANON_KEY` (You can find these under _"Project Settings"_ -> _"API"_ in your Supabase dashboard — under the _"URL"_ section, and anon key under _"Project API keys"_.)
+- For the value of `POWERSYNC_URL`, follow these steps:
+ 1. In the project tree on the PowerSync dashboard, right-click on the instance you created earlier.
+ 2. Click _"Edit instance"_.
+ 3. Click on _"Instance URL"_ to copy the value.
+
+
+## Build & Run the Flutter App
+
+- Run ``` flutter pub get ``` to install the necessary packages (in the root directory of the project.)
+- Invoke the ``` flutter run ``` command, and select either an Android device/emulator or iOS device/simulator as destination (_Note: PowerSync does not support Flutter web apps yet._)
+
+
+## Configuring Sync Rules - 2
+
+### Using Sync Rules to Enforce Permissions
+We have syncing working, but the sync rules are not enforcing the access rules from the domain in any way.
+
+It is better that we do not sync data to the client that the logged-in user is not allowed to see. We can use PowerSync sync rules to enforce permissions, so that users can only see and edit data that they are allowed to see and edit.
+
+First, we need to understand the permissions from the app domain model:
+
+- A **workspace** is created by a user — this user can always see and edit the workspace.
+- A **workspace** has a specific *visibility*: private (only the owner can see it), workspace (only owner and members can see it), or public (anyone can see it).
+- A **workspace** has a list of *members* (users) that can see and edit the workspace, if the workspace is not private.
+- A **board** is created by a user — this user can always see and edit the board as long as the user can still access that workspace
+- A **board** has a specific *visibility*: private (only the owner can see it), workspace (only owner and members belonging to the parent workspace can see it)
+- A user can see (and edit) any of the **cards** and **lists** belonging to a **board** that they have access to.
+- A user can see (and edit) any of the **checklists**, **comments**, and **attachments** belonging to a **card** that they have access to.
+
+Also have a look at `trelloappclone_flutter/lib/utils/service.dart` for the access patterns used by the app code.
+
+Let's explore how we can use PowerSync Sync Rules to enforce these permissions and access patterns.
+
+First we want to sync the relevant `trellouser` records synced to the local database. To enable lookups of users for adding as members to workspaces, we currently sync all user records. For a production app, we would ideally work via an API to invite members, and not worry about direct data lookups on the app side.
+
+```yaml
+bucket_definitions:
+ user_info:
+ # this allows syncing of all trellouser records so we can lookup users when adding members
+ data:
+ - SELECT * FROM trellouser
+```
+
+Then we want to look up all the workspaces (a) owned by this user, (b) where this user is a member, or (c) which are public.
+
+```yaml
+ by_workspace:
+ # the entities are filtered by workspaceId, thus linked to the workspaces (a) owned by this user, (b) where this user is a member, or (c) which are public
+ # Note: the quotes for "workspaceId" and "userId" is important, since otherwise Postgres does not deal well with non-lowercase identifiers
+ parameters:
+ - SELECT id as workspace_id FROM workspace WHERE
+ workspace."userId" = token_parameters.user_id
+ - SELECT "workspaceId" as workspace_id FROM member WHERE
+ member."userId" = token_parameters.user_id
+ - SELECT id as workspace_id FROM workspace WHERE
+ visibility = "Public"
+ data:
+ - SELECT * FROM workspace WHERE workspace.id = bucket.workspace_id
+ - SELECT * FROM board WHERE board."workspaceId" = bucket.workspace_id
+ - SELECT * FROM member WHERE member."workspaceId" = bucket.workspace_id
+ - SELECT * FROM listboard WHERE listboard."workspaceId" = bucket.workspace_id
+ - SELECT * FROM card WHERE card."workspaceId" = bucket.workspace_id
+ - SELECT * FROM checklist WHERE checklist."workspaceId" = bucket.workspace_id
+ - SELECT * FROM activity WHERE activity."workspaceId" = bucket.workspace_id
+ - SELECT * FROM comment WHERE comment."workspaceId" = bucket.workspace_id
+ - SELECT * FROM attachment WHERE attachment."workspaceId" = bucket.workspace_id
+ - SELECT * FROM board_label WHERE board_label."workspaceId" = bucket.workspace_id
+ - SELECT * FROM card_label WHERE card_label."workspaceId" = bucket.workspace_id
+```
+
+**To Configure the Improved Sync Rules, Follow These Steps:**
+
+- Copy the contents of `sync-rules-1.yaml`.
+- Paste this to `sync-rules.yaml` under your PowerSync project instance
+- Click _"Deploy sync rules"_.
+- Confirm in the dialog and wait a couple of minutes for the deployment to complete.
+
+Now you can run the app again, and it should now only sync the subset of data that a logged in user actually has access to.
+
+### Importing / Generating Data
+
+When you run the app, after logging in, you will start without any workspaces or boards. It is possible to generate a workspace with sample boards and cards in order to make it easier to have enough data to experiment with, without having to manually create every item.
+
+- Sign up and log in to the app.
+- In the home view, tap on the "+" floating button in the lower right corner.
+- Tap on _"Sample Workspace"_ and give it a name — this will create a new workspace, with multiple boards with lists, and a random number of cards with checklists, comments and activities for each list.
+
+
+
+### Structure
+
+* The data models are in `lib/models`)
+* A PowerSync client that makes use of the PowerSync Flutter SDK and adds a few convenience methods for the app use cases (`lib/protocol/powersync.dart`)
+* A `DataClient` API that can be used from the app code, and provides the higher level data API. (`lib/protocol/data_client.dart`)
+
+Two important files to point out as well are:
+
+* `lib/schema.dart` defines the SQLite schema to use for the local synced datastore, and this maps to the model classes.
+* `lib/app_config.dart` contains the tokens and URLs needed to connect to PowerSync and Supabase.
+
+
+### Listening to Updated Data
+
+The PowerSync SDK makes use of `watch` queries to listen for changes to synced data. When the SDK syncs updated data from the server, and it matches the query, it will send an event out, allowing e.g. an app view (via `StreamBuilder`) or some other state management class to respond.
+
+As an example look at `trelloappclone_flutter/lib/utils/service.dart`:
+
+```dart
+ Stream> getWorkspacesStream() {
+ return dataClient.workspace.watchWorkspacesByUser(userId: trello.user.id).map((workspaces) {
+ trello.setWorkspaces(workspaces);
+ return workspaces;
+ });
+ }
+```
+
+This in turn makes use of the `_WorkspaceRepository` class:
+```dart
+ Stream> watchWorkspacesByUser({required String userId}) {
+ //First we get the workspaces
+ return client.getDBExecutor().watch('''
+ SELECT * FROM workspace WHERE userId = ?
+ ''', parameters: [userId]).asyncMap((event) async {
+ List workspaces = event.map((row) => Workspace.fromRow(row)).toList();
+
+ //Then we get the members for each workspace
+ for (Workspace workspace in workspaces) {
+ List members = await client.member.getMembersByWorkspace(workspaceId: workspace.id);
+ workspace.members = members;
+ }
+ return workspaces;
+ });
+ }
+```
+
+Now we can simply make use of a `StreamBuilder` to have a view component that updates whenever a matching workspace is updated:
+
+```dart
+ StreamBuilder(
+ stream: getWorkspacesStream(),
+ builder:
+ (BuildContext context, AsyncSnapshot> snapshot) {
+ if (snapshot.hasData) {
+ List children = snapshot.data as List;
+
+ if (children.isNotEmpty) {
+ return SingleChildScrollView(
+ child:
+ Column(children: buildWorkspacesAndBoards(children)));
+ }
+ } else {
+ return Container();
+ }
+ })
+```
+
+### Doing a Transaction
+
+It is possible to ensure that a number of related entities are updated in an atomic database transaction. As an example of this, look at `DataClient.archiveCardsInList`:
+
+```dart
+ /// Archive cards in and return how many were archived
+ /// This happens in a transaction
+ Future archiveCardsInList(Listboard list) async {
+ if (list.cards == null || list.cards!.isEmpty) {
+ return 0;
+ }
+
+ //start a write transaction
+ return client.getDBExecutor().writeTransaction((sqlContext) async {
+ List cards = list.cards!;
+ int numCards = cards.length;
+
+ //we set each of the cards in the list to archived = true
+ sqlContext.executeBatch('''
+ UPDATE card
+ SET archived = 1
+ WHERE id = ?
+ '''
+ , cards.map((card) => [card.id]).toList());
+
+ //touch listboard to trigger update via stream listeners on Listboard
+ sqlContext.execute('''
+ UPDATE listboard
+ SET archived = 0
+ WHERE id = ?
+ ''', [list.id]);
+
+ list.cards = [];
+ return numCards;
+ //end of transaction
+ }, debugContext: 'archiveCardsInList');
+ }
+```
+
+The above code is invoked if you choose to archive all the cards in a list from the list popup menu.
+
+### Changes from Original Trello Clone App
+
+The app code was forked from the [Serverpod + Flutter Tutorial](https://github.com/Mobterest/serverpod_flutter_tutorial) code. It was changed in the following ways to facilitate the PowerSync integration:
+
+- Updated data model so that all `id` fields are Strings, and use UUIDs (it was auto-increment integer fields in the original app)
+- Updated data model so that all entities refer to the `workspaceId` of workspace in which it was created (this facilitates the Sync Rules)
+
+## Next
+
+Below is a list of things that can be implemented to enhance the functionality and experience of this app:
+
+* Update Workspace + Board edit views to use actual data and update the entity
+* Get Comments & Checklists working properly
+* Enhance state management - e.g. let `TrelloProvider` listen to streams, and notify changes, to simplify views
+* Get the attachments to actually work (using Supabase files upload/download)
+
+## Feedback
+
+- Feel free to send feedback. Feature requests are always welcome. If there's anything you'd like to chat about, please [join our Discord](https://discord.gg/powersync).
+
+## Acknowledgements
+
+This tutorial is based on the [Serverpod + Flutter Tutorial](https://github.com/Mobterest/serverpod_flutter_tutorial) by [Mobterest](https://www.instagram.com/mobterest/)
diff --git a/demos/supabase-trello/analysis_options.yaml b/demos/supabase-trello/analysis_options.yaml
new file mode 100644
index 00000000..61b6c4de
--- /dev/null
+++ b/demos/supabase-trello/analysis_options.yaml
@@ -0,0 +1,29 @@
+# This file configures the analyzer, which statically analyzes Dart code to
+# check for errors, warnings, and lints.
+#
+# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
+# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
+# invoked from the command line by running `flutter analyze`.
+
+# The following line activates a set of recommended lints for Flutter apps,
+# packages, and plugins designed to encourage good coding practices.
+include: package:flutter_lints/flutter.yaml
+
+linter:
+ # The lint rules applied to this project can be customized in the
+ # section below to disable rules from the `package:flutter_lints/flutter.yaml`
+ # included above or to enable additional rules. A list of all available lints
+ # and their documentation is published at
+ # https://dart-lang.github.io/linter/lints/index.html.
+ #
+ # Instead of disabling a lint rule for the entire project in the
+ # section below, it can also be suppressed for a single line of code
+ # or a specific dart file by using the `// ignore: name_of_lint` and
+ # `// ignore_for_file: name_of_lint` syntax on the line or in the file
+ # producing the lint.
+ rules:
+ # avoid_print: false # Uncomment to disable the `avoid_print` rule
+ # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/demos/supabase-trello/android/.gitignore b/demos/supabase-trello/android/.gitignore
new file mode 100644
index 00000000..6f568019
--- /dev/null
+++ b/demos/supabase-trello/android/.gitignore
@@ -0,0 +1,13 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
+key.properties
+**/*.keystore
+**/*.jks
diff --git a/demos/supabase-trello/android/app/build.gradle b/demos/supabase-trello/android/app/build.gradle
new file mode 100644
index 00000000..21dc0f37
--- /dev/null
+++ b/demos/supabase-trello/android/app/build.gradle
@@ -0,0 +1,65 @@
+plugins {
+ id "com.android.application"
+ id "kotlin-android"
+ id "dev.flutter.flutter-gradle-plugin"
+}
+
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+ flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+ flutterVersionName = '1.0'
+}
+
+android {
+ namespace "com.example.trelloappclone_flutter"
+ compileSdkVersion flutter.compileSdkVersion
+ ndkVersion flutter.ndkVersion
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId "com.example.trelloappclone_flutter"
+ // You can update the following values to match your application needs.
+ // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
+ minSdkVersion 23
+ targetSdkVersion flutter.targetSdkVersion
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source '../..'
+}
diff --git a/demos/supabase-trello/android/app/src/debug/AndroidManifest.xml b/demos/supabase-trello/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 00000000..399f6981
--- /dev/null
+++ b/demos/supabase-trello/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/demos/supabase-trello/android/app/src/main/AndroidManifest.xml b/demos/supabase-trello/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..e1dc4071
--- /dev/null
+++ b/demos/supabase-trello/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demos/supabase-trello/android/app/src/main/kotlin/com/example/trelloappclone_flutter/MainActivity.kt b/demos/supabase-trello/android/app/src/main/kotlin/com/example/trelloappclone_flutter/MainActivity.kt
new file mode 100644
index 00000000..875d532e
--- /dev/null
+++ b/demos/supabase-trello/android/app/src/main/kotlin/com/example/trelloappclone_flutter/MainActivity.kt
@@ -0,0 +1,6 @@
+package com.example.trelloappclone_flutter
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity: FlutterActivity() {
+}
diff --git a/demos/supabase-trello/android/app/src/main/res/drawable-v21/launch_background.xml b/demos/supabase-trello/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 00000000..f74085f3
--- /dev/null
+++ b/demos/supabase-trello/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/demos/supabase-trello/android/app/src/main/res/drawable/launch_background.xml b/demos/supabase-trello/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 00000000..304732f8
--- /dev/null
+++ b/demos/supabase-trello/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/demos/supabase-trello/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/demos/supabase-trello/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..db77bb4b
Binary files /dev/null and b/demos/supabase-trello/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/demos/supabase-trello/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/demos/supabase-trello/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..17987b79
Binary files /dev/null and b/demos/supabase-trello/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/demos/supabase-trello/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/demos/supabase-trello/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..09d43914
Binary files /dev/null and b/demos/supabase-trello/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/demos/supabase-trello/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/demos/supabase-trello/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..d5f1c8d3
Binary files /dev/null and b/demos/supabase-trello/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/demos/supabase-trello/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/demos/supabase-trello/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..4d6372ee
Binary files /dev/null and b/demos/supabase-trello/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/demos/supabase-trello/android/app/src/main/res/values-night/styles.xml b/demos/supabase-trello/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 00000000..06952be7
--- /dev/null
+++ b/demos/supabase-trello/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/demos/supabase-trello/android/app/src/main/res/values/styles.xml b/demos/supabase-trello/android/app/src/main/res/values/styles.xml
new file mode 100644
index 00000000..cb1ef880
--- /dev/null
+++ b/demos/supabase-trello/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/demos/supabase-trello/android/app/src/profile/AndroidManifest.xml b/demos/supabase-trello/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 00000000..399f6981
--- /dev/null
+++ b/demos/supabase-trello/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/demos/supabase-trello/android/build.gradle b/demos/supabase-trello/android/build.gradle
new file mode 100644
index 00000000..bc157bd1
--- /dev/null
+++ b/demos/supabase-trello/android/build.gradle
@@ -0,0 +1,18 @@
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+ project.evaluationDependsOn(':app')
+}
+
+tasks.register("clean", Delete) {
+ delete rootProject.buildDir
+}
diff --git a/demos/supabase-trello/android/gradle.properties b/demos/supabase-trello/android/gradle.properties
new file mode 100644
index 00000000..94adc3a3
--- /dev/null
+++ b/demos/supabase-trello/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/demos/supabase-trello/android/gradle/wrapper/gradle-wrapper.properties b/demos/supabase-trello/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..348c409e
--- /dev/null
+++ b/demos/supabase-trello/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip
diff --git a/demos/supabase-trello/android/settings.gradle b/demos/supabase-trello/android/settings.gradle
new file mode 100644
index 00000000..e9862544
--- /dev/null
+++ b/demos/supabase-trello/android/settings.gradle
@@ -0,0 +1,25 @@
+pluginManagement {
+ def flutterSdkPath = {
+ def properties = new Properties()
+ file("local.properties").withInputStream { properties.load(it) }
+ def flutterSdkPath = properties.getProperty("flutter.sdk")
+ assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+ return flutterSdkPath
+ }()
+
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
+
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
+plugins {
+ id "dev.flutter.flutter-plugin-loader" version "1.0.0"
+ id "com.android.application" version "8.7.0" apply false
+ id "org.jetbrains.kotlin.android" version "1.7.0" apply false
+}
+
+include ":app"
diff --git a/demos/supabase-trello/assets/landing.jpg b/demos/supabase-trello/assets/landing.jpg
new file mode 100644
index 00000000..7614ae9f
Binary files /dev/null and b/demos/supabase-trello/assets/landing.jpg differ
diff --git a/demos/supabase-trello/assets/trello-logo.png b/demos/supabase-trello/assets/trello-logo.png
new file mode 100644
index 00000000..a8068fd6
Binary files /dev/null and b/demos/supabase-trello/assets/trello-logo.png differ
diff --git a/demos/supabase-trello/devtools_options.yaml b/demos/supabase-trello/devtools_options.yaml
new file mode 100644
index 00000000..fa0b357c
--- /dev/null
+++ b/demos/supabase-trello/devtools_options.yaml
@@ -0,0 +1,3 @@
+description: This file stores settings for Dart & Flutter DevTools.
+documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
+extensions:
diff --git a/demos/supabase-trello/ios/.gitignore b/demos/supabase-trello/ios/.gitignore
new file mode 100644
index 00000000..7a7f9873
--- /dev/null
+++ b/demos/supabase-trello/ios/.gitignore
@@ -0,0 +1,34 @@
+**/dgph
+*.mode1v3
+*.mode2v3
+*.moved-aside
+*.pbxuser
+*.perspectivev3
+**/*sync/
+.sconsign.dblite
+.tags*
+**/.vagrant/
+**/DerivedData/
+Icon?
+**/Pods/
+**/.symlinks/
+profile
+xcuserdata
+**/.generated/
+Flutter/App.framework
+Flutter/Flutter.framework
+Flutter/Flutter.podspec
+Flutter/Generated.xcconfig
+Flutter/ephemeral/
+Flutter/app.flx
+Flutter/app.zip
+Flutter/flutter_assets/
+Flutter/flutter_export_environment.sh
+ServiceDefinitions.json
+Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!default.mode1v3
+!default.mode2v3
+!default.pbxuser
+!default.perspectivev3
diff --git a/demos/supabase-trello/ios/Flutter/AppFrameworkInfo.plist b/demos/supabase-trello/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 00000000..7c569640
--- /dev/null
+++ b/demos/supabase-trello/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ App
+ CFBundleIdentifier
+ io.flutter.flutter.app
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ App
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ MinimumOSVersion
+ 12.0
+
+
diff --git a/demos/supabase-trello/ios/Flutter/Debug.xcconfig b/demos/supabase-trello/ios/Flutter/Debug.xcconfig
new file mode 100644
index 00000000..ec97fc6f
--- /dev/null
+++ b/demos/supabase-trello/ios/Flutter/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include "Generated.xcconfig"
diff --git a/demos/supabase-trello/ios/Flutter/Release.xcconfig b/demos/supabase-trello/ios/Flutter/Release.xcconfig
new file mode 100644
index 00000000..c4855bfe
--- /dev/null
+++ b/demos/supabase-trello/ios/Flutter/Release.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include "Generated.xcconfig"
diff --git a/demos/supabase-trello/ios/Podfile b/demos/supabase-trello/ios/Podfile
new file mode 100644
index 00000000..885313b0
--- /dev/null
+++ b/demos/supabase-trello/ios/Podfile
@@ -0,0 +1,47 @@
+# Uncomment this line to define a global platform for your project
+platform :ios, '12.0'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
+ end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
+ end
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_ios_podfile_setup
+
+target 'Runner' do
+ use_frameworks!
+ use_modular_headers!
+
+ flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+ target 'RunnerTests' do
+ inherit! :search_paths
+ end
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ flutter_additional_ios_build_settings(target)
+ target.build_configurations.each do |config|
+ config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0'
+ end
+ end
+end
diff --git a/demos/supabase-trello/ios/Podfile.lock b/demos/supabase-trello/ios/Podfile.lock
new file mode 100644
index 00000000..4ca14fbb
--- /dev/null
+++ b/demos/supabase-trello/ios/Podfile.lock
@@ -0,0 +1,136 @@
+PODS:
+ - app_links (0.0.2):
+ - Flutter
+ - DKImagePickerController/Core (4.3.9):
+ - DKImagePickerController/ImageDataManager
+ - DKImagePickerController/Resource
+ - DKImagePickerController/ImageDataManager (4.3.9)
+ - DKImagePickerController/PhotoGallery (4.3.9):
+ - DKImagePickerController/Core
+ - DKPhotoGallery
+ - DKImagePickerController/Resource (4.3.9)
+ - DKPhotoGallery (0.0.19):
+ - DKPhotoGallery/Core (= 0.0.19)
+ - DKPhotoGallery/Model (= 0.0.19)
+ - DKPhotoGallery/Preview (= 0.0.19)
+ - DKPhotoGallery/Resource (= 0.0.19)
+ - SDWebImage
+ - SwiftyGif
+ - DKPhotoGallery/Core (0.0.19):
+ - DKPhotoGallery/Model
+ - DKPhotoGallery/Preview
+ - SDWebImage
+ - SwiftyGif
+ - DKPhotoGallery/Model (0.0.19):
+ - SDWebImage
+ - SwiftyGif
+ - DKPhotoGallery/Preview (0.0.19):
+ - DKPhotoGallery/Model
+ - DKPhotoGallery/Resource
+ - SDWebImage
+ - SwiftyGif
+ - DKPhotoGallery/Resource (0.0.19):
+ - SDWebImage
+ - SwiftyGif
+ - file_picker (0.0.1):
+ - DKImagePickerController/PhotoGallery
+ - Flutter
+ - Flutter (1.0.0)
+ - image_picker_ios (0.0.1):
+ - Flutter
+ - path_provider_foundation (0.0.1):
+ - Flutter
+ - FlutterMacOS
+ - powersync-sqlite-core (0.3.7)
+ - powersync_flutter_libs (0.0.1):
+ - Flutter
+ - powersync-sqlite-core (~> 0.3.6)
+ - SDWebImage (5.20.0):
+ - SDWebImage/Core (= 5.20.0)
+ - SDWebImage/Core (5.20.0)
+ - shared_preferences_foundation (0.0.1):
+ - Flutter
+ - FlutterMacOS
+ - sqlite3 (3.47.2):
+ - sqlite3/common (= 3.47.2)
+ - sqlite3/common (3.47.2)
+ - sqlite3/dbstatvtab (3.47.2):
+ - sqlite3/common
+ - sqlite3/fts5 (3.47.2):
+ - sqlite3/common
+ - sqlite3/perf-threadsafe (3.47.2):
+ - sqlite3/common
+ - sqlite3/rtree (3.47.2):
+ - sqlite3/common
+ - sqlite3_flutter_libs (0.0.1):
+ - Flutter
+ - FlutterMacOS
+ - sqlite3 (~> 3.47.2)
+ - sqlite3/dbstatvtab
+ - sqlite3/fts5
+ - sqlite3/perf-threadsafe
+ - sqlite3/rtree
+ - SwiftyGif (5.4.5)
+ - url_launcher_ios (0.0.1):
+ - Flutter
+
+DEPENDENCIES:
+ - app_links (from `.symlinks/plugins/app_links/ios`)
+ - file_picker (from `.symlinks/plugins/file_picker/ios`)
+ - Flutter (from `Flutter`)
+ - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
+ - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
+ - powersync_flutter_libs (from `.symlinks/plugins/powersync_flutter_libs/ios`)
+ - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
+ - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`)
+ - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
+
+SPEC REPOS:
+ trunk:
+ - DKImagePickerController
+ - DKPhotoGallery
+ - powersync-sqlite-core
+ - SDWebImage
+ - sqlite3
+ - SwiftyGif
+
+EXTERNAL SOURCES:
+ app_links:
+ :path: ".symlinks/plugins/app_links/ios"
+ file_picker:
+ :path: ".symlinks/plugins/file_picker/ios"
+ Flutter:
+ :path: Flutter
+ image_picker_ios:
+ :path: ".symlinks/plugins/image_picker_ios/ios"
+ path_provider_foundation:
+ :path: ".symlinks/plugins/path_provider_foundation/darwin"
+ powersync_flutter_libs:
+ :path: ".symlinks/plugins/powersync_flutter_libs/ios"
+ shared_preferences_foundation:
+ :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
+ sqlite3_flutter_libs:
+ :path: ".symlinks/plugins/sqlite3_flutter_libs/darwin"
+ url_launcher_ios:
+ :path: ".symlinks/plugins/url_launcher_ios/ios"
+
+SPEC CHECKSUMS:
+ app_links: e7a6750a915a9e161c58d91bc610e8cd1d4d0ad0
+ DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
+ DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
+ file_picker: 15fd9539e4eb735dc54bae8c0534a7a9511a03de
+ Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
+ image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
+ path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
+ powersync-sqlite-core: b9bcd19fa191d3d6f3f85c0573c41799d654cfa7
+ powersync_flutter_libs: eb2694b322e1e4ef9a0f1e32dee600847f21ccbc
+ SDWebImage: 73c6079366fea25fa4bb9640d5fb58f0893facd8
+ shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
+ sqlite3: 7559e33dae4c78538df563795af3a86fc887ee71
+ sqlite3_flutter_libs: 58ae36c0dd086395d066b4fe4de9cdca83e717b3
+ SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
+ url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
+
+PODFILE CHECKSUM: 9d8d1770d47a428fcb57a5d5f228a34e6d072ae7
+
+COCOAPODS: 1.16.2
diff --git a/demos/supabase-trello/ios/Runner.xcodeproj/project.pbxproj b/demos/supabase-trello/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..643e5165
--- /dev/null
+++ b/demos/supabase-trello/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,723 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 54;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 580DE0ECE5B255011C07A13C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2B5432F50F552DF6C1D0FA1 /* Pods_Runner.framework */; };
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+ 9F5E6CDBDFD984AD1CE17EBF /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C37744B39F4E3B36A0BBB0CE /* Pods_RunnerTests.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 97C146E61CF9000F007C117D /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 97C146ED1CF9000F007C117D;
+ remoteInfo = Runner;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 0FF24B675B56F022C3289F08 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
+ 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
+ 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 36C14A9DF8CBED78B8383618 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
+ 976B259CF18C36EB3B6F4F0F /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ A2E15785CC927C31AEF6BC6A /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; };
+ B0E8CE81B52F70CDF2A4F80B /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; };
+ C37744B39F4E3B36A0BBB0CE /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ DB8B11F7834AB262F47996E5 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
+ E2B5432F50F552DF6C1D0FA1 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 580DE0ECE5B255011C07A13C /* Pods_Runner.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ A9A2ECF3A7DDE6AA422CD674 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 9F5E6CDBDFD984AD1CE17EBF /* Pods_RunnerTests.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 331C8082294A63A400263BE5 /* RunnerTests */ = {
+ isa = PBXGroup;
+ children = (
+ 331C807B294A618700263BE5 /* RunnerTests.swift */,
+ );
+ path = RunnerTests;
+ sourceTree = "";
+ };
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ 331C8082294A63A400263BE5 /* RunnerTests */,
+ A7F3D089A45655143E0E6848 /* Pods */,
+ B312AFB09377AB967113F8FE /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ 331C8081294A63A400263BE5 /* RunnerTests.xctest */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+ A7F3D089A45655143E0E6848 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ DB8B11F7834AB262F47996E5 /* Pods-Runner.debug.xcconfig */,
+ 36C14A9DF8CBED78B8383618 /* Pods-Runner.release.xcconfig */,
+ 0FF24B675B56F022C3289F08 /* Pods-Runner.profile.xcconfig */,
+ A2E15785CC927C31AEF6BC6A /* Pods-RunnerTests.debug.xcconfig */,
+ B0E8CE81B52F70CDF2A4F80B /* Pods-RunnerTests.release.xcconfig */,
+ 976B259CF18C36EB3B6F4F0F /* Pods-RunnerTests.profile.xcconfig */,
+ );
+ path = Pods;
+ sourceTree = "";
+ };
+ B312AFB09377AB967113F8FE /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ E2B5432F50F552DF6C1D0FA1 /* Pods_Runner.framework */,
+ C37744B39F4E3B36A0BBB0CE /* Pods_RunnerTests.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 331C8080294A63A400263BE5 /* RunnerTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
+ buildPhases = (
+ 579312ED36242754EF3992BA /* [CP] Check Pods Manifest.lock */,
+ 331C807D294A63A400263BE5 /* Sources */,
+ 331C807F294A63A400263BE5 /* Resources */,
+ A9A2ECF3A7DDE6AA422CD674 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 331C8086294A63A400263BE5 /* PBXTargetDependency */,
+ );
+ name = RunnerTests;
+ productName = RunnerTests;
+ productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 5582B600ABA2BF2B04110F91 /* [CP] Check Pods Manifest.lock */,
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ B27A73ED59948A115A9CD70F /* [CP] Embed Pods Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 97C146E61CF9000F007C117D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1510;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 331C8080294A63A400263BE5 = {
+ CreatedOnToolsVersion = 14.0;
+ TestTargetID = 97C146ED1CF9000F007C117D;
+ };
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ LastSwiftMigration = 1100;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 97C146E51CF9000F007C117D;
+ productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 97C146ED1CF9000F007C117D /* Runner */,
+ 331C8080294A63A400263BE5 /* RunnerTests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 331C807F294A63A400263BE5 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 97C146EC1CF9000F007C117D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+ };
+ 5582B600ABA2BF2B04110F91 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 579312ED36242754EF3992BA /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ };
+ B27A73ED59948A115A9CD70F /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 331C807D294A63A400263BE5 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 97C146EA1CF9000F007C117D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 97C146ED1CF9000F007C117D /* Runner */;
+ targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C146FB1CF9000F007C117D /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 249021D3217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Profile;
+ };
+ 249021D4217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = K2CV557XCP;
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.journeyapps.trelloappcloneFlutter;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Profile;
+ };
+ 331C8088294A63A400263BE5 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = A2E15785CC927C31AEF6BC6A /* Pods-RunnerTests.debug.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.trelloappcloneFlutter.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+ };
+ name = Debug;
+ };
+ 331C8089294A63A400263BE5 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = B0E8CE81B52F70CDF2A4F80B /* Pods-RunnerTests.release.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.trelloappcloneFlutter.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+ };
+ name = Release;
+ };
+ 331C808A294A63A400263BE5 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 976B259CF18C36EB3B6F4F0F /* Pods-RunnerTests.profile.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.trelloappcloneFlutter.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+ };
+ name = Profile;
+ };
+ 97C147031CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 97C147061CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = K2CV557XCP;
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.journeyapps.trelloappcloneFlutter;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Debug;
+ };
+ 97C147071CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = K2CV557XCP;
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.journeyapps.trelloappcloneFlutter;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 331C8088294A63A400263BE5 /* Debug */,
+ 331C8089294A63A400263BE5 /* Release */,
+ 331C808A294A63A400263BE5 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147031CF9000F007C117D /* Debug */,
+ 97C147041CF9000F007C117D /* Release */,
+ 249021D3217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147061CF9000F007C117D /* Debug */,
+ 97C147071CF9000F007C117D /* Release */,
+ 249021D4217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}
diff --git a/demos/supabase-trello/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/demos/supabase-trello/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..919434a6
--- /dev/null
+++ b/demos/supabase-trello/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/demos/supabase-trello/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/demos/supabase-trello/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/demos/supabase-trello/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/demos/supabase-trello/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/demos/supabase-trello/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 00000000..f9b0d7c5
--- /dev/null
+++ b/demos/supabase-trello/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/demos/supabase-trello/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/demos/supabase-trello/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 00000000..8e3ca5df
--- /dev/null
+++ b/demos/supabase-trello/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demos/supabase-trello/ios/Runner.xcworkspace/contents.xcworkspacedata b/demos/supabase-trello/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..21a3cc14
--- /dev/null
+++ b/demos/supabase-trello/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/demos/supabase-trello/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/demos/supabase-trello/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/demos/supabase-trello/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/demos/supabase-trello/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/demos/supabase-trello/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 00000000..f9b0d7c5
--- /dev/null
+++ b/demos/supabase-trello/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/demos/supabase-trello/ios/Runner/AppDelegate.swift b/demos/supabase-trello/ios/Runner/AppDelegate.swift
new file mode 100644
index 00000000..70693e4a
--- /dev/null
+++ b/demos/supabase-trello/ios/Runner/AppDelegate.swift
@@ -0,0 +1,13 @@
+import UIKit
+import Flutter
+
+@UIApplicationMain
+@objc class AppDelegate: FlutterAppDelegate {
+ override func application(
+ _ application: UIApplication,
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+ ) -> Bool {
+ GeneratedPluginRegistrant.register(with: self)
+ return super.application(application, didFinishLaunchingWithOptions: launchOptions)
+ }
+}
diff --git a/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000..d36b1fab
--- /dev/null
+++ b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,122 @@
+{
+ "images" : [
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "83.5x83.5",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-83.5x83.5@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "Icon-App-1024x1024@1x.png",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 00000000..dc9ada47
Binary files /dev/null and b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 00000000..7353c41e
Binary files /dev/null and b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 00000000..797d452e
Binary files /dev/null and b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 00000000..6ed2d933
Binary files /dev/null and b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 00000000..4cd7b009
Binary files /dev/null and b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 00000000..fe730945
Binary files /dev/null and b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 00000000..321773cd
Binary files /dev/null and b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 00000000..797d452e
Binary files /dev/null and b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 00000000..502f463a
Binary files /dev/null and b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 00000000..0ec30343
Binary files /dev/null and b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 00000000..0ec30343
Binary files /dev/null and b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 00000000..e9f5fea2
Binary files /dev/null and b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 00000000..84ac32ae
Binary files /dev/null and b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 00000000..8953cba0
Binary files /dev/null and b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 00000000..0467bf12
Binary files /dev/null and b/demos/supabase-trello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/demos/supabase-trello/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/demos/supabase-trello/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 00000000..0bedcf2f
--- /dev/null
+++ b/demos/supabase-trello/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/demos/supabase-trello/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/demos/supabase-trello/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 00000000..9da19eac
Binary files /dev/null and b/demos/supabase-trello/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ
diff --git a/demos/supabase-trello/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/demos/supabase-trello/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 00000000..9da19eac
Binary files /dev/null and b/demos/supabase-trello/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ
diff --git a/demos/supabase-trello/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/demos/supabase-trello/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 00000000..9da19eac
Binary files /dev/null and b/demos/supabase-trello/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ
diff --git a/demos/supabase-trello/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/demos/supabase-trello/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 00000000..89c2725b
--- /dev/null
+++ b/demos/supabase-trello/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
diff --git a/demos/supabase-trello/ios/Runner/Base.lproj/LaunchScreen.storyboard b/demos/supabase-trello/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 00000000..f2e259c7
--- /dev/null
+++ b/demos/supabase-trello/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demos/supabase-trello/ios/Runner/Base.lproj/Main.storyboard b/demos/supabase-trello/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 00000000..f3c28516
--- /dev/null
+++ b/demos/supabase-trello/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demos/supabase-trello/ios/Runner/Info.plist b/demos/supabase-trello/ios/Runner/Info.plist
new file mode 100644
index 00000000..22057481
--- /dev/null
+++ b/demos/supabase-trello/ios/Runner/Info.plist
@@ -0,0 +1,51 @@
+
+
+
+
+ CADisableMinimumFrameDurationOnPhone
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleDisplayName
+ Trelloappclone Flutter
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ trelloappclone_flutter
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSRequiresIPhoneOS
+
+ UIApplicationSupportsIndirectInputEvents
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+
+
diff --git a/demos/supabase-trello/ios/Runner/Runner-Bridging-Header.h b/demos/supabase-trello/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 00000000..308a2a56
--- /dev/null
+++ b/demos/supabase-trello/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1 @@
+#import "GeneratedPluginRegistrant.h"
diff --git a/demos/supabase-trello/ios/RunnerTests/RunnerTests.swift b/demos/supabase-trello/ios/RunnerTests/RunnerTests.swift
new file mode 100644
index 00000000..86a7c3b1
--- /dev/null
+++ b/demos/supabase-trello/ios/RunnerTests/RunnerTests.swift
@@ -0,0 +1,12 @@
+import Flutter
+import UIKit
+import XCTest
+
+class RunnerTests: XCTestCase {
+
+ func testExample() {
+ // If you add code to the Runner application, consider adding tests here.
+ // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
+ }
+
+}
diff --git a/demos/supabase-trello/lib/features/aboutboard/presentation/index.dart b/demos/supabase-trello/lib/features/aboutboard/presentation/index.dart
new file mode 100644
index 00000000..4f089aea
--- /dev/null
+++ b/demos/supabase-trello/lib/features/aboutboard/presentation/index.dart
@@ -0,0 +1,43 @@
+import 'package:flutter/material.dart';
+import 'package:trelloappclone_flutter/utils/constant.dart';
+
+class AboutBoard extends StatefulWidget {
+ const AboutBoard({super.key});
+
+ @override
+ State createState() => _AboutBoardState();
+}
+
+class _AboutBoardState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text("About this board"),
+ centerTitle: false,
+ ),
+ body: const Padding(
+ padding: EdgeInsets.all(20.0),
+ child: Column(children: [
+ Text(
+ "Made by",
+ style: TextStyle(fontWeight: FontWeight.w600),
+ ),
+ ListTile(
+ leading: CircleAvatar(),
+ title: Text("Jane Doe"),
+ subtitle: Text("@janedoe"),
+ ),
+ Text(
+ "Description",
+ style: TextStyle(fontWeight: FontWeight.w600),
+ ),
+ Padding(
+ padding: EdgeInsets.only(top: 10.0),
+ child: Text(defaultDescription),
+ )
+ ]),
+ ),
+ );
+ }
+}
diff --git a/demos/supabase-trello/lib/features/activity/presentation/index.dart b/demos/supabase-trello/lib/features/activity/presentation/index.dart
new file mode 100644
index 00000000..6a15a08c
--- /dev/null
+++ b/demos/supabase-trello/lib/features/activity/presentation/index.dart
@@ -0,0 +1,67 @@
+import 'package:flutter/material.dart';
+import 'package:trelloappclone_flutter/utils/color.dart';
+import 'package:trelloappclone_flutter/models/activity.dart';
+import 'package:trelloappclone_flutter/models/card.dart';
+
+import '../../../utils/service.dart';
+
+class Activities extends StatefulWidget {
+ final Cardlist crd;
+ const Activities(this.crd, {super.key});
+
+ @override
+ State createState() => _ActivitiesState();
+}
+
+class _ActivitiesState extends State with Service {
+ List activities = [];
+
+ @override
+ Widget build(BuildContext context) {
+ return FutureBuilder(
+ initialData: activities,
+ future: getActivities(widget.crd),
+ builder:
+ (BuildContext context, AsyncSnapshot> snapshot) {
+ if (snapshot.hasData) {
+ List children = snapshot.data as List;
+
+ if (children.isNotEmpty) {
+ return ListView(
+ shrinkWrap: true, children: buildWidget(children));
+ }
+ }
+ return const SizedBox.shrink();
+ });
+ }
+
+ List buildWidget(List activities) {
+ List tiles = [];
+
+ for (int i = 0; i < activities.length; i++) {
+ tiles.add(ActivityTile(activity: activities[i].description));
+ }
+ return tiles;
+ }
+}
+
+class ActivityTile extends StatefulWidget {
+ final String activity;
+ const ActivityTile({required this.activity, super.key});
+
+ @override
+ State createState() => _ActivityTileState();
+}
+
+class _ActivityTileState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return ListTile(
+ leading: const CircleAvatar(
+ backgroundColor: brandColor,
+ ),
+ title: Text(widget.activity),
+ subtitle: const Text("01 Jan 2023 at 1:11 am"),
+ );
+ }
+}
diff --git a/demos/supabase-trello/lib/features/archivedcards/presentation/index.dart b/demos/supabase-trello/lib/features/archivedcards/presentation/index.dart
new file mode 100644
index 00000000..5702546d
--- /dev/null
+++ b/demos/supabase-trello/lib/features/archivedcards/presentation/index.dart
@@ -0,0 +1,49 @@
+import 'package:flutter/material.dart';
+
+import '../../../utils/color.dart';
+
+class ArchivedCards extends StatefulWidget {
+ const ArchivedCards({super.key});
+
+ @override
+ State createState() => _ArchivedCardsState();
+}
+
+class _ArchivedCardsState extends State {
+ bool select = false;
+ int selected = 0;
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ leading: (select)
+ ? IconButton(
+ onPressed: () {},
+ icon: const Icon(
+ Icons.close,
+ size: 30,
+ ),
+ )
+ : null,
+ title: Text((select) ? "&selected selected" : "Archived cards"),
+ centerTitle: false,
+ actions: [
+ (select)
+ ? TextButton(
+ onPressed: () {},
+ child: const Text(
+ "SEND TO BOARD",
+ style: TextStyle(color: whiteShade),
+ ))
+ : IconButton(
+ onPressed: () {},
+ icon: const Icon(Icons.check_circle_outline))
+ ],
+ ),
+ body: const Center(
+ child: Text("No archived cards"),
+ ),
+ );
+ }
+}
diff --git a/demos/supabase-trello/lib/features/archivedlists/presentation/index.dart b/demos/supabase-trello/lib/features/archivedlists/presentation/index.dart
new file mode 100644
index 00000000..76f5181c
--- /dev/null
+++ b/demos/supabase-trello/lib/features/archivedlists/presentation/index.dart
@@ -0,0 +1,48 @@
+import 'package:flutter/material.dart';
+import 'package:trelloappclone_flutter/utils/color.dart';
+
+class ArchivedLists extends StatefulWidget {
+ const ArchivedLists({super.key});
+
+ @override
+ State createState() => _ArchivedListsState();
+}
+
+class _ArchivedListsState extends State {
+ bool select = false;
+ int selected = 0;
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ leading: (select)
+ ? IconButton(
+ onPressed: () {},
+ icon: const Icon(
+ Icons.close,
+ size: 30,
+ ),
+ )
+ : null,
+ title: Text((select) ? "&selected selected" : "Archived lists"),
+ centerTitle: false,
+ actions: [
+ (select)
+ ? TextButton(
+ onPressed: () {},
+ child: const Text(
+ "SEND TO BOARD",
+ style: TextStyle(color: whiteShade),
+ ))
+ : IconButton(
+ onPressed: () {},
+ icon: const Icon(Icons.check_circle_outline))
+ ],
+ ),
+ body: const Center(
+ child: Text("No archived lists"),
+ ),
+ );
+ }
+}
diff --git a/demos/supabase-trello/lib/features/board/domain/board_arguments.dart b/demos/supabase-trello/lib/features/board/domain/board_arguments.dart
new file mode 100644
index 00000000..7b7720d5
--- /dev/null
+++ b/demos/supabase-trello/lib/features/board/domain/board_arguments.dart
@@ -0,0 +1,9 @@
+import 'package:trelloappclone_flutter/models/board.dart';
+import 'package:trelloappclone_flutter/models/workspace.dart';
+
+class BoardArguments {
+ final Board board;
+ final Workspace workspace;
+
+ BoardArguments(this.board, this.workspace);
+}
diff --git a/demos/supabase-trello/lib/features/board/presentation/boarditemobject.dart b/demos/supabase-trello/lib/features/board/presentation/boarditemobject.dart
new file mode 100644
index 00000000..62f5b5b4
--- /dev/null
+++ b/demos/supabase-trello/lib/features/board/presentation/boarditemobject.dart
@@ -0,0 +1,13 @@
+import 'package:trelloappclone_flutter/models/card_label.dart';
+
+class BoardItemObject {
+ String? title;
+ bool? hasDescription;
+ List? cardLabels;
+
+ BoardItemObject({this.title, this.hasDescription, this.cardLabels}) {
+ title ??= "";
+ hasDescription ??= false;
+ cardLabels ??= [];
+ }
+}
diff --git a/demos/supabase-trello/lib/features/board/presentation/boardlistobject.dart b/demos/supabase-trello/lib/features/board/presentation/boardlistobject.dart
new file mode 100644
index 00000000..0b19b482
--- /dev/null
+++ b/demos/supabase-trello/lib/features/board/presentation/boardlistobject.dart
@@ -0,0 +1,13 @@
+import 'boarditemobject.dart';
+
+class BoardListObject {
+ String? title;
+ String? listId;
+ List? items;
+
+ BoardListObject({this.title, this.listId, this.items}) {
+ listId ??="0";
+ title ??= "";
+ items ??= [];
+ }
+}
diff --git a/demos/supabase-trello/lib/features/board/presentation/index.dart b/demos/supabase-trello/lib/features/board/presentation/index.dart
new file mode 100644
index 00000000..b71b38a8
--- /dev/null
+++ b/demos/supabase-trello/lib/features/board/presentation/index.dart
@@ -0,0 +1,471 @@
+import 'dart:ffi';
+
+import 'package:flutter/material.dart';
+import 'package:status_alert/status_alert.dart';
+import 'package:trelloappclone_flutter/features/carddetails/domain/card_detail_arguments.dart';
+import 'package:trelloappclone_flutter/features/carddetails/presentation/index.dart';
+import 'package:trelloappclone_flutter/utils/color.dart';
+import 'package:trelloappclone_flutter/widgets/thirdparty/board_item.dart';
+import 'package:trelloappclone_flutter/widgets/thirdparty/board_list.dart';
+import 'package:trelloappclone_flutter/widgets/thirdparty/boardview.dart';
+import 'package:trelloappclone_flutter/widgets/thirdparty/boardview_controller.dart';
+import 'package:trelloappclone_flutter/models/listboard.dart';
+import 'package:trelloappclone_flutter/models/card.dart';
+
+import '../../../main.dart';
+import '../../../utils/config.dart';
+import '../../../utils/service.dart';
+import '../../../utils/widgets.dart';
+import '../domain/board_arguments.dart';
+import 'boarditemobject.dart';
+import 'boardlistobject.dart';
+
+class BoardScreen extends StatefulWidget {
+ const BoardScreen({super.key});
+
+ @override
+ State createState() => _BoardScreenState();
+
+ static const routeName = '/board';
+}
+
+class _BoardScreenState extends State with Service {
+ BoardViewController boardViewController = BoardViewController();
+ bool showCard = false;
+ bool show = false;
+ List lists = [];
+ final TextEditingController nameController = TextEditingController();
+ Map textEditingControllers = {};
+ Map showtheCard = {};
+ int selectedList = 0;
+ int selectedCard = 0;
+ late double width;
+
+ @override
+ Widget build(BuildContext context) {
+ width = MediaQuery.of(context).size.width * 0.7;
+ final args = ModalRoute.of(context)!.settings.arguments as BoardArguments;
+ trello.setSelectedBoard(args.board);
+ trello.setSelectedWorkspace(args.workspace);
+
+ return WillPopScope(
+ onWillPop: () async {
+ Navigator.pushNamed(context, "/home");
+ return false;
+ },
+ child: Scaffold(
+ appBar: (!show && !showCard)
+ ? AppBar(
+ backgroundColor: brandColor,
+ centerTitle: false,
+ title: Text(args.board.name),
+ actions: [
+ IconButton(
+ onPressed: () {
+ Navigator.pushNamed(context, '/boardmenu');
+ },
+ icon: const Icon(Icons.more_horiz))
+ ],
+ )
+ : AppBar(
+ leading: IconButton(
+ onPressed: () {
+ setState(() {
+ nameController.clear();
+ textEditingControllers[selectedList]!.clear();
+ show = false;
+ showCard = false;
+ showtheCard[selectedCard] = false;
+ });
+ },
+ icon: const Icon(Icons.close)),
+ title: Text((show) ? "Add list" : "Add card"),
+ centerTitle: false,
+ actions: [
+ IconButton(
+ onPressed: () {
+ if (show) {
+ addList(Listboard(
+ id: randomUuid(),
+ workspaceId: args.workspace.id,
+ boardId: args.board.id,
+ userId: trello.user.id,
+ name: nameController.text,
+ order: trello.lstbrd.length));
+ nameController.clear();
+ setState(() {
+ show = false;
+ });
+ } else {
+ addCard(Cardlist(
+ id: randomUuid(),
+ workspaceId: args.workspace.id,
+ listId: trello.lstbrd[selectedList].id,
+ userId: trello.user.id,
+ name:
+ textEditingControllers[selectedList]!.text,
+ rank:
+ trello.lstbrd[selectedList].cards!.length));
+ textEditingControllers[selectedList]!.clear();
+ setState(() {
+ showCard = false;
+ showtheCard[selectedCard] = false;
+ });
+ }
+ },
+ icon: const Icon(Icons.check))
+ ],
+ ),
+ body: Padding(
+ padding: const EdgeInsets.all(10.0),
+ child: StreamBuilder(
+ stream: getListsByBoardStream(args.board),
+ builder: (BuildContext context,
+ AsyncSnapshot> snapshot) {
+ if (snapshot.hasData) {
+ List listBoards =
+ snapshot.data as List;
+ return BoardView(
+ lists: loadBoardView(listBoards),
+ boardViewController: boardViewController,
+ );
+ }
+ return const SizedBox.shrink();
+ })),
+ ));
+ }
+
+ Widget buildBoardItem(
+ BoardItemObject itemObject, List data) {
+ return BoardItem(
+ onStartDragItem: (listIndex, itemIndex, state) {},
+ onDropItem: (listIndex, itemIndex, oldListIndex, oldItemIndex, state) {
+ // if listIndex is null, then item was dropped outside of list reset the state
+ if (listIndex == null) {
+ return;
+ }
+
+ if (itemIndex == null || itemIndex > data[listIndex].items!.length) {
+ return;
+ }
+
+ // Move item to new list
+ var item = data[oldListIndex!].items?[oldItemIndex!];
+ data[oldListIndex].items!.removeAt(oldItemIndex!);
+ data[listIndex].items!.insert(itemIndex, item!);
+
+ var card = trello.lstbrd[oldListIndex].cards![oldItemIndex];
+
+ // update card listId
+ card.listId = trello.lstbrd[listIndex].id;
+ updateCard(card);
+
+ trello.lstbrd[oldListIndex].cards!.removeAt(oldItemIndex);
+ trello.lstbrd[listIndex].cards!.insert(itemIndex, card);
+
+ // reset rank based on index
+ trello.lstbrd[listIndex].cards!.asMap().forEach((index, card) {
+ card.rank = index;
+ updateCard(card);
+ });
+ },
+ onTapItem: (listIndex, itemIndex, state) {
+ Navigator.pushNamed(context, CardDetails.routeName,
+ arguments: CardDetailArguments(
+ trello.lstbrd[listIndex].cards![itemIndex],
+ trello.selectedBoard,
+ trello.lstbrd[listIndex]))
+ .then((value) => setState(() {}));
+ },
+ item: Card(
+ color: Colors.white,
+ elevation: 0,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(10.0),
+ ),
+ child: Column(children: [
+ Padding(
+ padding: const EdgeInsets.fromLTRB(10, 8, 8, 8),
+ child: Align(
+ alignment: Alignment.centerLeft,
+ child: Text(
+ itemObject.title!,
+ ),
+ ),
+ ),
+ Wrap(
+ children: [
+ // Add a horizontal space
+ const SizedBox(width: 0),
+ // Example labels with colored Chips
+ ...itemObject.cardLabels!.map((cardLabel) => Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 4, vertical: 2), // Horizontal margin
+ child: LabelDiplay(
+ color: trello.selectedBoard.boardLabels!
+ .firstWhere((boardLabel) =>
+ boardLabel.id == cardLabel.boardLabelId)
+ .color,
+ label: trello.selectedBoard.boardLabels!
+ .firstWhere((boardLabel) =>
+ boardLabel.id == cardLabel.boardLabelId)
+ .title))),
+ ],
+ ),
+ //Add icon to the column if card has description
+ if (itemObject.hasDescription!)
+ const Padding(
+ padding: EdgeInsets.fromLTRB(8, 2, 8, 8),
+ child: Align(
+ alignment: Alignment.centerLeft,
+ child: Icon(Icons.description, size: 16),
+ ),
+ ),
+ ])));
+ }
+
+ Widget _createBoardList(
+ BoardListObject list, List data, int index) {
+ List items = [];
+ for (int i = 0; i < list.items!.length; i++) {
+ items.insert(i, buildBoardItem(list.items![i], data) as BoardItem);
+ }
+
+ textEditingControllers.putIfAbsent(index, () => TextEditingController());
+ showtheCard.putIfAbsent(index, () => false);
+
+ items.insert(
+ list.items!.length,
+ BoardItem(
+ onTapItem: (listIndex, itemIndex, state) {
+ setState(() {
+ selectedList = listIndex!;
+ selectedCard = index;
+ showCard = true;
+ showtheCard[index] = true;
+ });
+ },
+ item: (!showtheCard[index]!)
+ ? ListTile(
+ leading: const Text.rich(TextSpan(
+ children: [
+ WidgetSpan(
+ child: Icon(
+ Icons.add,
+ size: 19,
+ color: whiteShade,
+ )),
+ WidgetSpan(
+ child: SizedBox(
+ width: 5,
+ ),
+ ),
+ TextSpan(
+ text: "Add card",
+ style: TextStyle(color: whiteShade)),
+ ],
+ )),
+ trailing: IconButton(
+ icon: const Icon(
+ Icons.image,
+ color: whiteShade,
+ ),
+ onPressed: () {},
+ ),
+ )
+ : Padding(
+ padding: const EdgeInsets.all(10.0),
+ child: TextField(
+ controller: textEditingControllers[index],
+ decoration: const InputDecoration(hintText: "Card name"),
+ ),
+ ),
+ ));
+
+ return BoardList(
+ onStartDragList: (listIndex) {},
+ onTapList: (listIndex) async {},
+ onDropList: (listIndex, oldListIndex) {
+ var tmpList = data[oldListIndex!];
+
+ data.removeAt(oldListIndex);
+ data.insert(listIndex!, tmpList);
+
+ updateListOrder(tmpList.listId!, listIndex);
+
+ var movedList = trello.lstbrd[oldListIndex];
+
+ trello.lstbrd.removeAt(oldListIndex);
+ trello.lstbrd.insert(listIndex, movedList);
+
+ // reset rank based on index
+ trello.lstbrd.asMap().forEach((index, list) {
+ updateListOrder(list.id, index);
+ });
+ },
+ headerBackgroundColor: brandColor,
+ backgroundColor: brandColor,
+ header: [
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.all(5),
+ child: ListTile(
+ leading: SizedBox(
+ width: 180,
+ child: Text(
+ overflow: TextOverflow.ellipsis,
+ softWrap: false,
+ maxLines: 2,
+ list.title!,
+ style: const TextStyle(
+ fontSize: 16, fontWeight: FontWeight.w500),
+ ),
+ ),
+ trailing: PopupMenuButton(
+ child: const Icon(Icons.more_vert),
+ itemBuilder: (BuildContext context) =>
+ >[
+ PopupMenuItem(
+ child: ListTile(
+ enabled: false,
+ title: Text(listMenu[1]),
+ ),
+ ),
+ PopupMenuItem(
+ child: ListTile(
+ enabled: false,
+ title: Text(listMenu[2]),
+ ),
+ ),
+ PopupMenuItem(
+ child: ListTile(
+ enabled: false,
+ title: Text(listMenu[3]),
+ ),
+ ),
+ const PopupMenuItem(
+ child: Divider(
+ height: 1,
+ thickness: 1,
+ )),
+ PopupMenuItem(
+ child: ListTile(
+ enabled: false,
+ title: Text(listMenu[4]),
+ trailing:
+ const Icon(Icons.keyboard_arrow_right),
+ ),
+ ),
+ const PopupMenuItem(
+ child: Divider(
+ height: 1,
+ thickness: 1,
+ )),
+ PopupMenuItem(
+ child: ListTile(
+ enabled: false,
+ title: Text(listMenu[5]),
+ ),
+ ),
+ PopupMenuItem(
+ child: ListTile(
+ title: Text(listMenu[6]),
+ onTap: () {
+ archiveCardsInList(trello.lstbrd[index])
+ .then((numCardsArchived) {
+ StatusAlert.show(context,
+ duration: const Duration(seconds: 2),
+ title:
+ '$numCardsArchived Cards Archived',
+ configuration: const IconConfiguration(
+ icon: Icons.archive_outlined,
+ color: brandColor),
+ maxWidth: 260);
+ Navigator.of(context).pop();
+ });
+ },
+ ),
+ ),
+ PopupMenuItem(
+ child: ListTile(
+ enabled: false,
+ title: Text(listMenu[7]),
+ ),
+ ),
+ ]),
+ ))),
+ ],
+ items: items,
+ );
+ }
+
+ List generateBoardListObject(List lists) {
+ final List listData = [];
+
+ for (int i = 0; i < lists.length; i++) {
+ listData.add(BoardListObject(
+ title: lists[i].name,
+ listId: lists[i].id,
+ items: generateBoardItemObject(lists[i].cards!)));
+ }
+
+ return listData;
+ }
+
+ List generateBoardItemObject(List crds) {
+ final List items = [];
+ for (int i = 0; i < crds.length; i++) {
+ items.add(BoardItemObject(
+ title: crds[i].name,
+ cardLabels: crds[i].cardLabels,
+ hasDescription: (crds[i].description != null) ? true : false));
+ }
+ return items;
+ }
+
+ List loadBoardView(List Listboards) {
+ List data = generateBoardListObject(Listboards);
+ lists = [];
+
+ for (int i = 0; i < data.length; i++) {
+ lists.add(_createBoardList(data[i], data, i) as BoardList);
+ }
+
+ lists.insert(
+ data.length,
+ BoardList(
+ items: [
+ BoardItem(
+ item: GestureDetector(
+ onTap: () {
+ setState(() {
+ show = true;
+ });
+ },
+ child: Container(
+ alignment: Alignment.center,
+ width: width,
+ height: 50,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(10.0),
+ color: brandColor,
+ ),
+ child: (!show)
+ ? const Text(
+ "Add list",
+ style: TextStyle(color: whiteShade),
+ )
+ : Padding(
+ padding: const EdgeInsets.all(10.0),
+ child: TextField(
+ controller: nameController,
+ decoration:
+ const InputDecoration(hintText: "List name"),
+ ),
+ )),
+ ))
+ ],
+ ));
+ return lists;
+ }
+}
diff --git a/demos/supabase-trello/lib/features/boardbackground/presentation/index.dart b/demos/supabase-trello/lib/features/boardbackground/presentation/index.dart
new file mode 100644
index 00000000..6b08897a
--- /dev/null
+++ b/demos/supabase-trello/lib/features/boardbackground/presentation/index.dart
@@ -0,0 +1,59 @@
+import 'package:flutter/material.dart';
+import 'package:trelloappclone_flutter/utils/config.dart';
+import '../../../main.dart';
+
+class BoardBackground extends StatefulWidget {
+ const BoardBackground({super.key});
+
+ @override
+ State createState() => _BoardBackgroundState();
+}
+
+class _BoardBackgroundState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text("Board background"),
+ centerTitle: false,
+ ),
+ body: GridView.builder(
+ gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
+ maxCrossAxisExtent: 200,
+ childAspectRatio: 1,
+ crossAxisSpacing: 3,
+ mainAxisSpacing: 20),
+ itemCount: backgrounds.length,
+ itemBuilder: (BuildContext cxt, index) {
+ return GestureDetector(
+ onTap: () {
+ setState(() {
+ trello.setSelectedBg(backgrounds[index]);
+ });
+ },
+ child: Stack(
+ children: [
+ Container(
+ alignment: Alignment.center,
+ decoration: BoxDecoration(
+ color: Color(int.parse(
+ backgrounds[index].substring(1, 7),
+ radix: 16) +
+ 0xff000000),
+ borderRadius: BorderRadius.circular(5)),
+ ),
+ (backgrounds[index] == trello.selectedBackground)
+ ? const Center(
+ child: Icon(
+ Icons.check,
+ color: Colors.white,
+ size: 50,
+ ),
+ )
+ : const SizedBox.shrink()
+ ],
+ ));
+ }),
+ );
+ }
+}
diff --git a/demos/supabase-trello/lib/features/boardmenu/presentation/index.dart b/demos/supabase-trello/lib/features/boardmenu/presentation/index.dart
new file mode 100644
index 00000000..82badf5b
--- /dev/null
+++ b/demos/supabase-trello/lib/features/boardmenu/presentation/index.dart
@@ -0,0 +1,202 @@
+import 'package:flutter/material.dart';
+import 'package:trelloappclone_flutter/features/visibility/presentation/index.dart';
+import 'package:trelloappclone_flutter/main.dart';
+import 'package:trelloappclone_flutter/utils/color.dart';
+
+class BoardMenu extends StatefulWidget {
+ const BoardMenu({super.key});
+
+ @override
+ State createState() => _BoardMenuState();
+}
+
+class _BoardMenuState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ leading: IconButton(
+ onPressed: () {
+ Navigator.pop(context);
+ },
+ icon: const Icon(Icons.close),
+ ),
+ title: const Text("Board menu"),
+ centerTitle: false,
+ ),
+ body: SingleChildScrollView(
+ child: Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.all(20.0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Container(
+ decoration: const BoxDecoration(
+ color: brandColor,
+ borderRadius: BorderRadius.all(Radius.circular(10.0))),
+ child: IconButton(
+ onPressed: () {},
+ icon: const Icon(
+ Icons.star_border,
+ size: 30,
+ ),
+ ),
+ ),
+ Container(
+ decoration: const BoxDecoration(
+ color: brandColor,
+ borderRadius: BorderRadius.all(Radius.circular(10.0))),
+ child: IconButton(
+ onPressed: () {
+ showDialog(
+ context: context,
+ builder: (BuildContext context) {
+ return const BoardVisibility();
+ });
+ },
+ icon: const Icon(
+ Icons.people,
+ size: 30,
+ ),
+ ),
+ ),
+ Container(
+ decoration: const BoxDecoration(
+ color: brandColor,
+ borderRadius: BorderRadius.all(Radius.circular(10.0))),
+ child: IconButton(
+ onPressed: () {
+ Navigator.pushNamed(context, "/copyboard");
+ },
+ icon: const Icon(
+ Icons.copy,
+ size: 30,
+ ),
+ ),
+ ),
+ Container(
+ decoration: const BoxDecoration(
+ color: brandColor,
+ borderRadius: BorderRadius.all(Radius.circular(10.0))),
+ child: IconButton(
+ onPressed: () {
+ Navigator.pushNamed(context, "/boardsettings");
+ },
+ icon: const Icon(
+ Icons.more_horiz,
+ size: 30,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ Container(
+ padding: const EdgeInsets.only(top: 10.0),
+ child: ListTile(
+ tileColor: whiteShade,
+ leading: const Icon(Icons.person_outline),
+ title: const Padding(
+ padding: EdgeInsets.only(top: 15.0, bottom: 15.0),
+ child: Text("Members"),
+ ),
+ subtitle: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(bottom: 18.0),
+ child: InkWell(
+ onTap: () {
+ Navigator.pushNamed(context, '/members');
+ },
+ child: Row(
+ children: buildMemberAvatars(),
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(bottom: 15.0),
+ child: SizedBox(
+ height: 37,
+ width: MediaQuery.of(context).size.width * 0.7,
+ child: ElevatedButton(
+ style: ElevatedButton.styleFrom(
+ backgroundColor: brandColor),
+ onPressed: () {
+ Navigator.pushNamed(context, "/invitemember");
+ },
+ child: const Text("Invite to workspace"),
+ ),
+ ),
+ )
+ ],
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(top: 15.0),
+ child: Container(
+ color: whiteShade,
+ child: ListTile(
+ leading: const Icon(Icons.info_outline),
+ title: const Text("About this board"),
+ onTap: () {
+ Navigator.pushNamed(context, '/aboutboard');
+ },
+ ),
+ ),
+ ),
+ // Padding(
+ // padding: const EdgeInsets.only(top: 15.0),
+ // child: Container(
+ // color: whiteShade,
+ // child: ListTile(
+ // leading: const Icon(Icons.rocket),
+ // title: const Text("Power-Ups"),
+ // onTap: () {
+ // Navigator.pushNamed(context, '/powerups');
+ // },
+ // ),
+ // )),
+ // Padding(
+ // padding: const EdgeInsets.only(top: 15.0),
+ // child: Container(
+ // color: whiteShade,
+ // child: ListTile(
+ // leading: const Icon(Icons.push_pin_outlined),
+ // title: const Text("Pin to home screen"),
+ // onTap: () {},
+ // ),
+ // )),
+ // const Padding(
+ // padding: EdgeInsets.all(15.0),
+ // child: Text(
+ // "Activity",
+ // style: TextStyle(fontWeight: FontWeight.bold),
+ // ),
+ // ),
+ // //TODO: figure out what is going on here
+ // Activities(Cardlist(
+ // id: "todo", workspaceId: trello.selectedWorkspace.id, listId: "todo", userId: trello.user.id, name: ""))
+ ],
+ )),
+ );
+ }
+
+ List buildMemberAvatars() {
+ List avatars = [];
+
+ trello.selectedWorkspace.members?.forEach((member) {
+ avatars.add(CircleAvatar(
+ backgroundColor: brandColor,
+ child: Text(member.name[0].toUpperCase()),
+ ));
+ avatars.add(const SizedBox(
+ width: 4,
+ ));
+ });
+ return avatars;
+ }
+}
diff --git a/demos/supabase-trello/lib/features/boardsettings/presentation/index.dart b/demos/supabase-trello/lib/features/boardsettings/presentation/index.dart
new file mode 100644
index 00000000..b3826325
--- /dev/null
+++ b/demos/supabase-trello/lib/features/boardsettings/presentation/index.dart
@@ -0,0 +1,206 @@
+import 'package:flutter/material.dart';
+import 'package:trelloappclone_flutter/utils/color.dart';
+import 'package:trelloappclone_flutter/utils/config.dart';
+
+import '../../../utils/widgets.dart';
+import '../../closeboard/presentation/index.dart';
+
+class BoardSettings extends StatefulWidget {
+ const BoardSettings({super.key});
+
+ @override
+ State createState() => _BoardSettingsState();
+}
+
+class _BoardSettingsState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(title: const Text("Board settings")),
+ body: SingleChildScrollView(
+ child: Column(
+ children: [
+ const BlueRectangle(),
+ Padding(
+ padding: const EdgeInsets.only(top: 15.0),
+ child: Container(
+ color: whiteShade,
+ child: const ListTile(
+ leading: Text("Name"),
+ trailing: Text("Board 1"),
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(top: 3.0),
+ child: Container(
+ color: whiteShade,
+ child: const ListTile(
+ leading: Text("Workspace"),
+ trailing: Text("Workspace 1"),
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(top: 3.0),
+ child: Container(
+ color: whiteShade,
+ child: ListTile(
+ leading: const Text("Background"),
+ trailing: ColorSquare(
+ bckgrd: backgrounds[0],
+ ),
+ onTap: () {
+ Navigator.pushNamed(context, "/boardbackground");
+ },
+ ),
+ ),
+ ),
+ // Padding(
+ // padding: const EdgeInsets.only(top: 3.0),
+ // child: Container(
+ // color: whiteShade,
+ // child: ListTile(
+ // leading: const Text("Enable card cover images"),
+ // trailing: Switch(value: true, onChanged: ((value) {})),
+ // ),
+ // ),
+ // ),
+ // Padding(
+ // padding: const EdgeInsets.only(top: 3.0),
+ // child: Container(
+ // color: whiteShade,
+ // child: ListTile(
+ // leading: const Text("Watch"),
+ // trailing: Switch(value: false, onChanged: ((value) {}))),
+ // ),
+ // ),
+ // Padding(
+ // padding: const EdgeInsets.only(top: 3.0),
+ // child: Container(
+ // color: whiteShade,
+ // child: ListTile(
+ // leading: const Text("Available offline"),
+ // trailing: Switch(value: false, onChanged: ((value) {}))),
+ // ),
+ // ),
+ // Padding(
+ // padding: const EdgeInsets.only(top: 3.0),
+ // child: Container(
+ // color: whiteShade,
+ // child: ListTile(
+ // leading: const Text("Edit labels"),
+ // onTap: () {
+ // showDialog(
+ // context: context,
+ // builder: (BuildContext context) {
+ // return const EditLabels();
+ // });
+ // },
+ // ),
+ // ),
+ // ),
+ // Padding(
+ // padding: const EdgeInsets.only(top: 3.0),
+ // child: Container(
+ // color: whiteShade,
+ // child: ListTile(
+ // leading: const Text("Email-to-board settings"),
+ // onTap: () {
+ // Navigator.pushNamed(context, "/emailtoboard");
+ // },
+ // ),
+ // ),
+ // ),
+ // Padding(
+ // padding: const EdgeInsets.only(top: 3.0),
+ // child: Container(
+ // color: whiteShade,
+ // child: ListTile(
+ // leading: const Text("Archived cards"),
+ // onTap: () {
+ // Navigator.pushNamed(context, "/archivedcards");
+ // },
+ // ),
+ // ),
+ // ),
+ // Padding(
+ // padding: const EdgeInsets.only(top: 3.0),
+ // child: Container(
+ // color: whiteShade,
+ // child: ListTile(
+ // leading: const Text("Archived lists"),
+ // onTap: () {
+ // Navigator.pushNamed(context, "/archivedlists");
+ // },
+ // ),
+ // ),
+ // ),
+ Padding(
+ padding: const EdgeInsets.only(top: 15.0),
+ child: Container(
+ color: whiteShade,
+ child: const ListTile(
+ leading: Text("Visibility"),
+ trailing: Text("Public"),
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(top: 3.0),
+ child: Container(
+ color: whiteShade,
+ child: const ListTile(
+ leading: Text("Commenting"),
+ trailing: Text("Members"),
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(top: 3.0),
+ child: Container(
+ color: whiteShade,
+ child: const ListTile(
+ leading: Text("Adding members"),
+ trailing: Text("Members"),
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(top: 15.0),
+ child: Container(
+ color: whiteShade,
+ child: ListTile(
+ leading: const Text("Self join"),
+ trailing: Switch(
+ value: true,
+ onChanged: ((value) {}),
+ )),
+ ),
+ ),
+ const Padding(
+ padding: EdgeInsets.only(top: 10.0),
+ child:
+ Text("Any Workspace member can edit and join the board")),
+ Padding(
+ padding: const EdgeInsets.only(top: 15.0, bottom: 50),
+ child: Container(
+ color: whiteShade,
+ child: ListTile(
+ leading: const Text("Close board"),
+ onTap: () {
+ showDialog(
+ context: context,
+ builder: (BuildContext context) {
+ return const CloseBoard();
+ });
+ },
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/demos/supabase-trello/lib/features/carddetails/domain/card_detail_arguments.dart b/demos/supabase-trello/lib/features/carddetails/domain/card_detail_arguments.dart
new file mode 100644
index 00000000..b76dfd32
--- /dev/null
+++ b/demos/supabase-trello/lib/features/carddetails/domain/card_detail_arguments.dart
@@ -0,0 +1,11 @@
+import 'package:trelloappclone_flutter/models/board.dart';
+import 'package:trelloappclone_flutter/models/card.dart';
+import 'package:trelloappclone_flutter/models/listboard.dart';
+
+class CardDetailArguments {
+ final Cardlist crd;
+ final Board brd;
+ final Listboard lst;
+
+ CardDetailArguments(this.crd, this.brd, this.lst);
+}
diff --git a/demos/supabase-trello/lib/features/carddetails/presentation/index.dart b/demos/supabase-trello/lib/features/carddetails/presentation/index.dart
new file mode 100644
index 00000000..4249b071
--- /dev/null
+++ b/demos/supabase-trello/lib/features/carddetails/presentation/index.dart
@@ -0,0 +1,376 @@
+import 'package:flutter/material.dart';
+import 'package:trelloappclone_flutter/features/activity/presentation/index.dart';
+import 'package:trelloappclone_flutter/models/checklist.dart';
+import 'package:trelloappclone_flutter/utils/color.dart';
+import 'package:trelloappclone_flutter/main.dart';
+
+import '../../../utils/service.dart';
+import '../../../utils/widgets.dart';
+import '../../editlabels/presentation/index.dart';
+import '../../viewmembers/presentation/index.dart';
+import '../domain/card_detail_arguments.dart';
+
+class CardDetails extends StatefulWidget {
+ const CardDetails({super.key});
+
+ @override
+ State createState() => _CardDetailsState();
+
+ static const routeName = '/carddetail';
+}
+
+class _CardDetailsState extends State with Service {
+ final TextEditingController descriptionController = TextEditingController();
+ final TextEditingController nameController = TextEditingController();
+ final TextEditingController checklistController = TextEditingController();
+ bool showChecklist = false;
+ bool addCardDescription = false;
+ bool editCardName = false;
+ Map checked = {};
+
+ @override
+ Widget build(BuildContext context) {
+ final args =
+ ModalRoute.of(context)!.settings.arguments as CardDetailArguments;
+
+ trello.setSelectedCard(args.crd);
+ descriptionController.text = args.crd.description ?? " ";
+ nameController.text = args.crd.name ?? " ";
+
+ return Scaffold(
+ appBar: (showChecklist || addCardDescription || editCardName)
+ ? AppBar(
+ leading: IconButton(
+ onPressed: () {
+ setState(() {
+ showChecklist = false;
+ addCardDescription = false;
+ editCardName = false;
+ });
+ },
+ icon: const Icon(Icons.close, size: 30),
+ ),
+ title: Text(() {
+ if (showChecklist) {
+ return "Add Checklist";
+ } else if (addCardDescription) {
+ return "Add card description";
+ } else if (editCardName) {
+ return "Edit card name";
+ } else {
+ return "";
+ }
+ }()),
+ actions: [
+ IconButton(
+ icon: const Icon(Icons.check),
+ onPressed: () {
+ if (showChecklist) {
+ createChecklist(Checklist(
+ id: randomUuid(),
+ workspaceId: args.crd.workspaceId,
+ cardId: args.crd.id,
+ name: checklistController.text,
+ status: false));
+ checklistController.clear();
+ setState(() {
+ showChecklist = false;
+ });
+ } else if (addCardDescription || editCardName) {
+ if (addCardDescription &&
+ descriptionController.text.isNotEmpty) {
+ args.crd.description = descriptionController.text;
+ }
+
+ if (editCardName && nameController.text.isNotEmpty) {
+ args.crd.name = nameController.text;
+ }
+
+ updateCard(args.crd);
+ descriptionController.clear();
+ nameController.clear();
+ setState(() {
+ addCardDescription = false;
+ editCardName = false;
+ });
+ }
+ },
+ )
+ ])
+ : AppBar(
+ leading: IconButton(
+ onPressed: () {
+ Navigator.pop(context);
+ },
+ icon: const Icon(Icons.close, size: 30),
+ ),
+ actions: [
+ PopupMenuButton(
+ itemBuilder: (context) {
+ return [
+ PopupMenuItem(
+ onTap: () => WidgetsBinding?.instance
+ ?.addPostFrameCallback((_) {
+ showDialog(
+ context: context,
+ builder: (BuildContext context) => AlertDialog(
+ title: const Text('Delete Card'),
+ content: const Text(
+ 'Are you sure you want to delete this card?'),
+ actions: [
+ TextButton(
+ onPressed: () =>
+ Navigator.pop(context, 'Cancel'),
+ child: const Text('Cancel'),
+ ),
+ TextButton(
+ onPressed: () => {
+ deleteCard(args.crd),
+ // Remove popup
+ Navigator.pop(context, 'Delete'),
+ // Go one view back
+ Navigator.pop(context, 'Delete'),
+ },
+ child: const Text('Delete'),
+ ),
+ ],
+ ));
+ }),
+ value: const Text("Delete Card"),
+ child: const ListTile(
+ leading: Icon(Icons.delete),
+ title: Text(
+ "Delete Card",
+ ),
+ ),
+ ),
+ ];
+ },
+ )
+ // IconButton(
+ // icon: const Icon(Icons.more_vert),
+ // onPressed: () {},
+ // )
+ ]),
+ body: SingleChildScrollView(
+ child: Padding(
+ padding: const EdgeInsets.all(10.0),
+ child:
+ Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
+ ListTile(
+ title: TextField(
+ style: const TextStyle(
+ fontSize: 20.0, // Set your desired font size here
+ ),
+ controller: nameController,
+ onTap: () {
+ setState(() {
+ editCardName = true;
+ });
+ },
+ decoration: const InputDecoration(hintText: "Edit card name"),
+ ),
+ ),
+ RichText(
+ text: TextSpan(
+ text: args.brd.name,
+ style: const TextStyle(
+ fontWeight: FontWeight.w600,
+ fontSize: 16,
+ color: themeColor),
+ children: [
+ const TextSpan(
+ text: ' in list ', style: TextStyle(fontSize: 12)),
+ TextSpan(text: args.lst.name)
+ ])),
+ const Padding(
+ padding: EdgeInsets.only(top: 8.0, bottom: 8.0),
+ child: Text(
+ "Quick actions",
+ style: TextStyle(fontWeight: FontWeight.w600),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(left: 8.0, right: 8.0),
+ child: Row(
+ children: [
+ SizedBox(
+ width: MediaQuery.of(context).size.width * 0.4,
+ child: ElevatedButton.icon(
+ onPressed: () {
+ setState(() {
+ showChecklist = true;
+ });
+ },
+ label: const Text("Add Checklist"),
+ icon: const CircleAvatar(
+ backgroundColor: brandColor,
+ radius: 15,
+ child: Icon(Icons.checklist),
+ ),
+ ),
+ ),
+ const Spacer(),
+ SizedBox(
+ width: MediaQuery.of(context).size.width * 0.4,
+ child: ElevatedButton.icon(
+ onPressed: null,
+ label: const Text("Add Attachment"),
+ icon: const CircleAvatar(
+ backgroundColor: brandColor,
+ radius: 15,
+ child: Icon(Icons.attachment),
+ ),
+ ),
+ )
+ ],
+ ),
+ ),
+ ListTile(
+ leading: const Icon(Icons.short_text),
+ title: TextField(
+ controller: descriptionController,
+ keyboardType: TextInputType.multiline,
+ minLines: 1,
+ maxLines: 1024,
+ onTap: () {
+ setState(() {
+ addCardDescription = true;
+ });
+ },
+ decoration:
+ const InputDecoration(hintText: "Add card description"),
+ ),
+ ),
+ ListTile(
+ leading: const Icon(Icons.label),
+ title: Row(
+ children: [
+ const Text("Labels"),
+ // Add a horizontal space
+ const SizedBox(width: 8),
+ // Example labels with colored Chips
+ ...trello.selectedCard!.cardLabels!.map(
+ (cardLabel) => Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 4), // Horizontal margin
+ child: LabelDiplay(
+ color: trello.selectedBoard.boardLabels!
+ .firstWhere((boardLabel) =>
+ boardLabel.id == cardLabel.boardLabelId)
+ .color,
+ label: trello.selectedBoard.boardLabels!
+ .firstWhere((boardLabel) =>
+ boardLabel.id == cardLabel.boardLabelId)
+ .title)),
+ ),
+ ],
+ ),
+ onTap: () {
+ final result = showDialog(
+ context: context,
+ builder: (BuildContext context) {
+ return EditLabels(cardId: args.crd.id);
+ });
+
+ result.then((value) {
+ setState(() {});
+ });
+ },
+ ),
+ ListTile(
+ leading: const Icon(Icons.person),
+ title: const Text("Members"),
+ onTap: () {
+ showDialog(
+ context: context,
+ builder: (BuildContext context) {
+ return const ViewMembers();
+ });
+ },
+ ),
+ ListTile(
+ leading: const Icon(Icons.date_range_outlined),
+ title: const Text("Start date"),
+ onTap: () {},
+ ),
+ ListTile(
+ leading: const Text("Checklist"),
+ trailing: IconButton(
+ onPressed: () {
+ setState(() {
+ deleteChecklist(args.crd);
+ });
+ },
+ icon: const Icon(Icons.delete)),
+ ),
+ FutureBuilder(
+ future: getChecklists(args.crd),
+ builder: ((context, snapshot) {
+ if (snapshot.hasData) {
+ List children = snapshot.data as List;
+
+ if (children.isNotEmpty) {
+ return Column(children: buildChecklists(children));
+ }
+ }
+ return const SizedBox.shrink();
+ })),
+ Visibility(
+ visible: showChecklist,
+ child: TextField(
+ controller: checklistController,
+ ),
+ ),
+ const Text("Activity"),
+ Activities(args.crd)
+ ]),
+ ),
+ ),
+ //TODO: Add this back in when we get comments working properly
+ // bottomNavigationBar: Container(
+ // padding: const EdgeInsets.only(bottom: 5.0),
+ // color: whiteShade,
+ // width: MediaQuery.of(context).size.width * 0.8,
+ // height: 80,
+ // child: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
+ // const CircleAvatar(),
+ // SizedBox(
+ // width: MediaQuery.of(context).size.width * 0.7,
+ // child: TextField(
+ // decoration: InputDecoration(
+ // hintText: "Add comment",
+ // suffix: IconButton(
+ // onPressed: () {}, icon: const Icon(Icons.send))),
+ // ),
+ // ),
+ // IconButton(onPressed: () {}, icon: const Icon(Icons.attachment))
+ // ]),
+ // ),
+ );
+ }
+
+ List buildChecklists(List chcklst) {
+ List lists = [];
+
+ for (int i = 0; i < chcklst.length; i++) {
+ checked.putIfAbsent(i, () => false);
+ checked[i] = chcklst[i].status;
+ lists.add(
+ CheckboxListTile(
+ title: Text(chcklst[i].name),
+ value: checked[i],
+ onChanged: (bool? value) {
+ setState(() {
+ checked[i] = value!;
+ });
+ chcklst[i].status = value!;
+ updateChecklist(chcklst[i]);
+ },
+ ),
+ );
+ }
+
+ return lists;
+ }
+}
diff --git a/demos/supabase-trello/lib/features/closeboard/presentation/index.dart b/demos/supabase-trello/lib/features/closeboard/presentation/index.dart
new file mode 100644
index 00000000..500b196e
--- /dev/null
+++ b/demos/supabase-trello/lib/features/closeboard/presentation/index.dart
@@ -0,0 +1,39 @@
+import 'package:flutter/material.dart';
+import 'package:trelloappclone_flutter/utils/color.dart';
+
+class CloseBoard extends StatefulWidget {
+ const CloseBoard({super.key});
+
+ @override
+ State createState() => _CloseBoardState();
+}
+
+class _CloseBoardState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return AlertDialog(
+ title: const Text("Board 1 is now closed"),
+ content: SizedBox(
+ height: 100,
+ child: Column(
+ children: [
+ SizedBox(
+ width: MediaQuery.of(context).size.width * 0.7,
+ child: ElevatedButton(
+ onPressed: () {}, child: const Text("Re-open")),
+ ),
+ SizedBox(
+ width: MediaQuery.of(context).size.width * 0.7,
+ child: OutlinedButton(
+ onPressed: () {},
+ child: const Text(
+ "Delete",
+ style: TextStyle(color: dangerColor),
+ )),
+ )
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/demos/supabase-trello/lib/features/copyboard/presentation/index.dart b/demos/supabase-trello/lib/features/copyboard/presentation/index.dart
new file mode 100644
index 00000000..af2c68eb
--- /dev/null
+++ b/demos/supabase-trello/lib/features/copyboard/presentation/index.dart
@@ -0,0 +1,115 @@
+import 'package:flutter/material.dart';
+
+import '../../../utils/color.dart';
+import '../../../utils/constant.dart';
+
+class CopyBoard extends StatefulWidget {
+ const CopyBoard({super.key});
+
+ @override
+ State createState() => _CopyBoardState();
+}
+
+class _CopyBoardState extends State {
+ final TextEditingController nameController = TextEditingController();
+ String? dropdownValue;
+ List workspaces = [];
+ Map? visibilityDropdownValue;
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ leading: IconButton(
+ onPressed: () {
+ Navigator.pop(context);
+ },
+ icon: const Icon(
+ Icons.close,
+ size: 30,
+ )),
+ ),
+ body: SingleChildScrollView(
+ child: Padding(
+ padding: const EdgeInsets.all(20.0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ TextFormField(
+ controller: nameController,
+ decoration: const InputDecoration(
+ border: UnderlineInputBorder(), labelText: "Board name"),
+ ),
+ const Text("Workspace"),
+ Padding(
+ padding: const EdgeInsets.only(top: 8.0),
+ child: DropdownButton(
+ isExpanded: true,
+ value: dropdownValue,
+ icon: const Icon(Icons.keyboard_arrow_down),
+ elevation: 16,
+ style: const TextStyle(color: brandColor),
+ underline: Container(
+ height: 2,
+ color: brandColor,
+ ),
+ onChanged: (String? value) {
+ // This is called when the user selects an item.
+ setState(() {
+ dropdownValue = value!;
+ });
+ },
+ items:
+ workspaces.map>((String value) {
+ return DropdownMenuItem(
+ value: value,
+ child: Text(value),
+ );
+ }).toList(),
+ ),
+ ),
+ const Text("Visibility"),
+ Padding(
+ padding: const EdgeInsets.only(top: 8.0),
+ child: DropdownButton