diff --git a/.gitignore b/.gitignore
index 3820a95..f815956 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,3 +43,5 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release
+
+TPB.md
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 49607f1..4f98aa6 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -24,6 +24,13 @@
+
+
+
+
+
+
+
diff --git a/lib/main.dart b/lib/main.dart
index 244a702..9510f1c 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,122 +1,329 @@
import 'package:flutter/material.dart';
+import 'package:supabase_flutter/supabase_flutter.dart';
-void main() {
- runApp(const MyApp());
+// ── Supabase credentials ──────────────────────────────────────────────
+const _supabaseUrl = 'https://yaopcyubateifnicpywp.supabase.co';
+// TODO: Paste your anon key from Supabase Dashboard → Settings → API.
+const _supabaseAnonKey = 'YOUR_ANON_KEY';
+
+Future main() async {
+ WidgetsFlutterBinding.ensureInitialized();
+
+ await Supabase.initialize(
+ url: _supabaseUrl,
+ anonKey: _supabaseAnonKey,
+ );
+
+ runApp(const HWHubApp());
}
-class MyApp extends StatelessWidget {
- const MyApp({super.key});
+/// Convenience accessor used throughout the app.
+final supabase = Supabase.instance.client;
+
+// ── Root App Widget ───────────────────────────────────────────────────
+class HWHubApp extends StatelessWidget {
+ const HWHubApp({super.key});
- // This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
- title: 'Flutter Demo',
+ title: 'HW Hub',
+ debugShowCheckedModeBanner: false,
theme: ThemeData(
- // This is the theme of your application.
- //
- // TRY THIS: Try running your application with "flutter run". You'll see
- // the application has a purple toolbar. Then, without quitting the app,
- // try changing the seedColor in the colorScheme below to Colors.green
- // and then invoke "hot reload" (save your changes or press the "hot
- // reload" button in a Flutter-supported IDE, or press "r" if you used
- // the command line to start the app).
- //
- // Notice that the counter didn't reset back to zero; the application
- // state is not lost during the reload. To reset the state, use hot
- // restart instead.
- //
- // This works for code too, not just values: Most code changes can be
- // tested with just a hot reload.
- colorScheme: .fromSeed(seedColor: Colors.deepPurple),
+ colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
+ useMaterial3: true,
),
- home: const MyHomePage(title: 'Flutter Demo Home Page'),
+ home: const AuthGate(),
);
}
}
-class MyHomePage extends StatefulWidget {
- const MyHomePage({super.key, required this.title});
-
- // This widget is the home page of your application. It is stateful, meaning
- // that it has a State object (defined below) that contains fields that affect
- // how it looks.
-
- // This class is the configuration for the state. It holds the values (in this
- // case the title) provided by the parent (in this case the App widget) and
- // used by the build method of the State. Fields in a Widget subclass are
- // always marked "final".
-
- final String title;
+// ── Auth Gate ─────────────────────────────────────────────────────────
+// Listens to Supabase auth state changes (including deep-link callbacks
+// for password resets) and routes to Login or Home.
+class AuthGate extends StatefulWidget {
+ const AuthGate({super.key});
@override
- State createState() => _MyHomePageState();
+ State createState() => _AuthGateState();
}
-class _MyHomePageState extends State {
- int _counter = 0;
+class _AuthGateState extends State {
+ bool _isLoading = true;
+ Session? _session;
- void _incrementCounter() {
- setState(() {
- // This call to setState tells the Flutter framework that something has
- // changed in this State, which causes it to rerun the build method below
- // so that the display can reflect the updated values. If we changed
- // _counter without calling setState(), then the build method would not be
- // called again, and so nothing would appear to happen.
- _counter++;
- });
+ @override
+ void initState() {
+ super.initState();
+
+ // 1. Check for an existing session on cold start.
+ _session = supabase.auth.currentSession;
+
+ // 2. Listen for auth state changes (sign-in, sign-out,
+ // password-recovery deep link, token refresh, etc.)
+ supabase.auth.onAuthStateChange.listen(
+ (AuthState authState) {
+ if (!mounted) return;
+ setState(() {
+ _session = authState.session;
+ });
+
+ // If the user just clicked a password-reset link from their email,
+ // Supabase fires a PASSWORD_RECOVERY event. We can navigate them
+ // to a "set new password" screen here.
+ if (authState.event == AuthChangeEvent.passwordRecovery) {
+ _showResetPasswordDialog();
+ }
+ },
+ onError: (error) {
+ if (!mounted) return;
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text('Auth error: $error'), backgroundColor: Colors.red),
+ );
+ },
+ );
+
+ // Done checking – remove the loading state.
+ setState(() => _isLoading = false);
+ }
+
+ /// Shows a dialog so the user can type a new password after clicking
+ /// the "Reset Password" link from their email.
+ Future _showResetPasswordDialog() async {
+ final passwordController = TextEditingController();
+
+ await showDialog(
+ context: context,
+ barrierDismissible: false,
+ builder: (ctx) => AlertDialog(
+ title: const Text('Set New Password'),
+ content: TextField(
+ controller: passwordController,
+ obscureText: true,
+ decoration: const InputDecoration(
+ labelText: 'New password',
+ border: OutlineInputBorder(),
+ ),
+ ),
+ actions: [
+ TextButton(
+ onPressed: () => Navigator.of(ctx).pop(),
+ child: const Text('Cancel'),
+ ),
+ ElevatedButton(
+ onPressed: () async {
+ final newPassword = passwordController.text.trim();
+ if (newPassword.length < 6) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(content: Text('Password must be at least 6 characters.')),
+ );
+ return;
+ }
+ try {
+ await supabase.auth.updateUser(
+ UserAttributes(password: newPassword),
+ );
+ if (!ctx.mounted) return;
+ Navigator.of(ctx).pop();
+ if (!mounted) return;
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(content: Text('Password updated successfully!')),
+ );
+ } on AuthException catch (e) {
+ if (!mounted) return;
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(e.message), backgroundColor: Colors.red),
+ );
+ }
+ },
+ child: const Text('Save'),
+ ),
+ ],
+ ),
+ );
+
+ passwordController.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ if (_isLoading) {
+ return const Scaffold(
+ body: Center(child: CircularProgressIndicator()),
+ );
+ }
+
+ if (_session != null) {
+ return const HomeScreen();
+ }
+
+ return const LoginScreen();
+ }
+}
+
+// ── Login Screen ──────────────────────────────────────────────────────
+class LoginScreen extends StatefulWidget {
+ const LoginScreen({super.key});
+
+ @override
+ State createState() => _LoginScreenState();
+}
+
+class _LoginScreenState extends State {
+ final _emailController = TextEditingController();
+ final _passwordController = TextEditingController();
+ bool _isLoading = false;
+
+ /// Sign in with email + password.
+ Future _signIn() async {
+ final email = _emailController.text.trim();
+ final password = _passwordController.text;
+ if (email.isEmpty || password.isEmpty) return;
+
+ setState(() => _isLoading = true);
+
+ try {
+ await supabase.auth.signInWithPassword(
+ email: email,
+ password: password,
+ );
+ // AuthGate listener will pick up the new session automatically.
+ } on AuthException catch (e) {
+ if (!mounted) return;
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(e.message), backgroundColor: Colors.red),
+ );
+ } finally {
+ if (mounted) setState(() => _isLoading = false);
+ }
+ }
+
+ /// Send a password-reset email. The link in the email uses the
+ /// hwcollector://login deep link so it opens back in the app,
+ /// which triggers the PASSWORD_RECOVERY event in AuthGate.
+ Future _forgotPassword() async {
+ final email = _emailController.text.trim();
+ if (email.isEmpty) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(content: Text('Enter your email first.')),
+ );
+ return;
+ }
+
+ try {
+ await supabase.auth.resetPasswordForEmail(
+ email,
+ redirectTo: 'hwcollector://login',
+ );
+ if (!mounted) return;
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(content: Text('Password reset email sent! Check your inbox.')),
+ );
+ } on AuthException catch (e) {
+ if (!mounted) return;
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(e.message), backgroundColor: Colors.red),
+ );
+ }
+ }
+
+ @override
+ void dispose() {
+ _emailController.dispose();
+ _passwordController.dispose();
+ super.dispose();
}
@override
Widget build(BuildContext context) {
- // This method is rerun every time setState is called, for instance as done
- // by the _incrementCounter method above.
- //
- // The Flutter framework has been optimized to make rerunning build methods
- // fast, so that you can just rebuild anything that needs updating rather
- // than having to individually change instances of widgets.
return Scaffold(
- appBar: AppBar(
- // TRY THIS: Try changing the color here to a specific color (to
- // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
- // change color while the other colors stay the same.
- backgroundColor: Theme.of(context).colorScheme.inversePrimary,
- // Here we take the value from the MyHomePage object that was created by
- // the App.build method, and use it to set our appbar title.
- title: Text(widget.title),
- ),
body: Center(
- // Center is a layout widget. It takes a single child and positions it
- // in the middle of the parent.
- child: Column(
- // Column is also a layout widget. It takes a list of children and
- // arranges them vertically. By default, it sizes itself to fit its
- // children horizontally, and tries to be as tall as its parent.
- //
- // Column has various properties to control how it sizes itself and
- // how it positions its children. Here we use mainAxisAlignment to
- // center the children vertically; the main axis here is the vertical
- // axis because Columns are vertical (the cross axis would be
- // horizontal).
- //
- // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
- // action in the IDE, or press "p" in the console), to see the
- // wireframe for each widget.
- mainAxisAlignment: .center,
- children: [
- const Text('You have pushed the button this many times:'),
- Text(
- '$_counter',
- style: Theme.of(context).textTheme.headlineMedium,
- ),
- ],
+ child: SingleChildScrollView(
+ padding: const EdgeInsets.symmetric(horizontal: 32),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const Icon(Icons.directions_car, size: 72, color: Colors.deepPurple),
+ const SizedBox(height: 16),
+ const Text(
+ 'Hot Wheels Tracker',
+ style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
+ ),
+ const SizedBox(height: 32),
+ TextField(
+ controller: _emailController,
+ keyboardType: TextInputType.emailAddress,
+ decoration: const InputDecoration(
+ labelText: 'Email',
+ border: OutlineInputBorder(),
+ ),
+ ),
+ const SizedBox(height: 12),
+ TextField(
+ controller: _passwordController,
+ obscureText: true,
+ decoration: const InputDecoration(
+ labelText: 'Password',
+ border: OutlineInputBorder(),
+ ),
+ onSubmitted: (_) => _signIn(),
+ ),
+ const SizedBox(height: 8),
+ Align(
+ alignment: Alignment.centerRight,
+ child: TextButton(
+ onPressed: _forgotPassword,
+ child: const Text('Forgot Password?'),
+ ),
+ ),
+ const SizedBox(height: 8),
+ SizedBox(
+ width: double.infinity,
+ child: ElevatedButton(
+ onPressed: _isLoading ? null : _signIn,
+ child: _isLoading
+ ? const SizedBox(
+ height: 20,
+ width: 20,
+ child: CircularProgressIndicator(strokeWidth: 2),
+ )
+ : const Text('Sign In'),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
+
+// ── Home Screen (placeholder) ─────────────────────────────────────────
+class HomeScreen extends StatelessWidget {
+ const HomeScreen({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ final user = supabase.auth.currentUser;
+
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('Garage is Online! 🏎️'),
+ actions: [
+ IconButton(
+ icon: const Icon(Icons.logout),
+ onPressed: () async {
+ await supabase.auth.signOut();
+ },
+ ),
+ ],
+ ),
+ body: Center(
+ child: Text(
+ 'Signed in as ${user?.email ?? 'unknown'}',
+ style: Theme.of(context).textTheme.titleMedium,
),
),
- floatingActionButton: FloatingActionButton(
- onPressed: _incrementCounter,
- tooltip: 'Increment',
- child: const Icon(Icons.add),
- ),
);
}
}
diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc
index e71a16d..3792af4 100644
--- a/linux/flutter/generated_plugin_registrant.cc
+++ b/linux/flutter/generated_plugin_registrant.cc
@@ -6,6 +6,14 @@
#include "generated_plugin_registrant.h"
+#include
+#include
void fl_register_plugins(FlPluginRegistry* registry) {
+ g_autoptr(FlPluginRegistrar) gtk_registrar =
+ fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin");
+ gtk_plugin_register_with_registrar(gtk_registrar);
+ g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
+ fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
+ url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
}
diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake
index 2e1de87..5d07423 100644
--- a/linux/flutter/generated_plugins.cmake
+++ b/linux/flutter/generated_plugins.cmake
@@ -3,6 +3,8 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
+ gtk
+ url_launcher_linux
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift
index cccf817..a620c94 100644
--- a/macos/Flutter/GeneratedPluginRegistrant.swift
+++ b/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -5,6 +5,12 @@
import FlutterMacOS
import Foundation
+import app_links
+import shared_preferences_foundation
+import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
+ AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
+ SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
+ UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
}
diff --git a/pubspec.lock b/pubspec.lock
index 764bc87..60d5d7b 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -1,6 +1,46 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
+ adaptive_number:
+ dependency: transitive
+ description:
+ name: adaptive_number
+ sha256: "3a567544e9b5c9c803006f51140ad544aedc79604fd4f3f2c1380003f97c1d77"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.0"
+ app_links:
+ dependency: transitive
+ description:
+ name: app_links
+ sha256: "5f88447519add627fe1cbcab4fd1da3d4fed15b9baf29f28b22535c95ecee3e8"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.4.1"
+ app_links_linux:
+ dependency: transitive
+ description:
+ name: app_links_linux
+ sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.3"
+ app_links_platform_interface:
+ dependency: transitive
+ description:
+ name: app_links_platform_interface
+ sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.2"
+ app_links_web:
+ dependency: transitive
+ description:
+ name: app_links_web
+ sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.4"
async:
dependency: transitive
description:
@@ -33,6 +73,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.2"
+ code_assets:
+ dependency: transitive
+ description:
+ name: code_assets
+ sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.0"
collection:
dependency: transitive
description:
@@ -41,6 +89,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.19.1"
+ convert:
+ dependency: transitive
+ description:
+ name: convert
+ sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.2"
+ crypto:
+ dependency: transitive
+ description:
+ name: crypto
+ sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.7"
cupertino_icons:
dependency: "direct main"
description:
@@ -49,6 +113,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.8"
+ dart_jsonwebtoken:
+ dependency: transitive
+ description:
+ name: dart_jsonwebtoken
+ sha256: "0de65691c1d736e9459f22f654ddd6fd8368a271d4e41aa07e53e6301eff5075"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.3.1"
+ ed25519_edwards:
+ dependency: transitive
+ description:
+ name: ed25519_edwards
+ sha256: "6ce0112d131327ec6d42beede1e5dfd526069b18ad45dcf654f15074ad9276cd"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.3.1"
fake_async:
dependency: transitive
description:
@@ -57,6 +137,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.3"
+ ffi:
+ dependency: transitive
+ description:
+ name: ffi
+ sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.0"
+ file:
+ dependency: transitive
+ description:
+ name: file
+ sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.0.1"
+ fixnum:
+ dependency: transitive
+ description:
+ name: fixnum
+ sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.1"
flutter:
dependency: "direct main"
description: flutter
@@ -75,6 +179,75 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
+ flutter_web_plugins:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ functions_client:
+ dependency: transitive
+ description:
+ name: functions_client
+ sha256: "94074d62167ae634127ef6095f536835063a7dc80f2b1aa306d2346ff9023996"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.5.0"
+ glob:
+ dependency: transitive
+ description:
+ name: glob
+ sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.3"
+ gotrue:
+ dependency: transitive
+ description:
+ name: gotrue
+ sha256: f7b52008311941a7c3e99f9590c4ee32dfc102a5442e43abf1b287d9f8cc39b2
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.18.0"
+ gtk:
+ dependency: transitive
+ description:
+ name: gtk
+ sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.0"
+ hooks:
+ dependency: transitive
+ description:
+ name: hooks
+ sha256: "7a08a0d684cb3b8fb604b78455d5d352f502b68079f7b80b831c62220ab0a4f6"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.1"
+ http:
+ dependency: transitive
+ description:
+ name: http
+ sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.6.0"
+ http_parser:
+ dependency: transitive
+ description:
+ name: http_parser
+ sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.1.2"
+ jwt_decode:
+ dependency: transitive
+ description:
+ name: jwt_decode
+ sha256: d2e9f68c052b2225130977429d30f187aa1981d789c76ad104a32243cfdebfbb
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.3.1"
leak_tracker:
dependency: transitive
description:
@@ -107,6 +280,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.1.0"
+ logging:
+ dependency: transitive
+ description:
+ name: logging
+ sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.0"
matcher:
dependency: transitive
description:
@@ -131,6 +312,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.17.0"
+ mime:
+ dependency: transitive
+ description:
+ name: mime
+ sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.0"
+ native_toolchain_c:
+ dependency: transitive
+ description:
+ name: native_toolchain_c
+ sha256: "89e83885ba09da5fdf2cdacc8002a712ca238c28b7f717910b34bcd27b0d03ac"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.17.4"
+ objective_c:
+ dependency: transitive
+ description:
+ name: objective_c
+ sha256: "100a1c87616ab6ed41ec263b083c0ef3261ee6cd1dc3b0f35f8ddfa4f996fe52"
+ url: "https://pub.dev"
+ source: hosted
+ version: "9.3.0"
path:
dependency: transitive
description:
@@ -139,6 +344,174 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.9.1"
+ path_provider:
+ dependency: transitive
+ description:
+ name: path_provider
+ sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.5"
+ path_provider_android:
+ dependency: transitive
+ description:
+ name: path_provider_android
+ sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.22"
+ path_provider_foundation:
+ dependency: transitive
+ description:
+ name: path_provider_foundation
+ sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.6.0"
+ path_provider_linux:
+ dependency: transitive
+ description:
+ name: path_provider_linux
+ sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.1"
+ path_provider_platform_interface:
+ dependency: transitive
+ description:
+ name: path_provider_platform_interface
+ sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.2"
+ path_provider_windows:
+ dependency: transitive
+ description:
+ name: path_provider_windows
+ sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.3.0"
+ platform:
+ dependency: transitive
+ description:
+ name: platform
+ sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.6"
+ plugin_platform_interface:
+ dependency: transitive
+ description:
+ name: plugin_platform_interface
+ sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.8"
+ pointycastle:
+ dependency: transitive
+ description:
+ name: pointycastle
+ sha256: "92aa3841d083cc4b0f4709b5c74fd6409a3e6ba833ffc7dc6a8fee096366acf5"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.0.0"
+ postgrest:
+ dependency: transitive
+ description:
+ name: postgrest
+ sha256: f4b6bb24b465c47649243ef0140475de8a0ec311dc9c75ebe573b2dcabb10460
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.6.0"
+ pub_semver:
+ dependency: transitive
+ description:
+ name: pub_semver
+ sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.0"
+ realtime_client:
+ dependency: transitive
+ description:
+ name: realtime_client
+ sha256: "5268afc208d02fb9109854d262c1ebf6ece224cd285199ae1d2f92d2ff49dbf1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.7.0"
+ retry:
+ dependency: transitive
+ description:
+ name: retry
+ sha256: "822e118d5b3aafed083109c72d5f484c6dc66707885e07c0fbcb8b986bba7efc"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.2"
+ rxdart:
+ dependency: transitive
+ description:
+ name: rxdart
+ sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.28.0"
+ shared_preferences:
+ dependency: transitive
+ description:
+ name: shared_preferences
+ sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.5.4"
+ shared_preferences_android:
+ dependency: transitive
+ description:
+ name: shared_preferences_android
+ sha256: cbc40be9be1c5af4dab4d6e0de4d5d3729e6f3d65b89d21e1815d57705644a6f
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.20"
+ shared_preferences_foundation:
+ dependency: transitive
+ description:
+ name: shared_preferences_foundation
+ sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.5.6"
+ shared_preferences_linux:
+ dependency: transitive
+ description:
+ name: shared_preferences_linux
+ sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.1"
+ shared_preferences_platform_interface:
+ dependency: transitive
+ description:
+ name: shared_preferences_platform_interface
+ sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.1"
+ shared_preferences_web:
+ dependency: transitive
+ description:
+ name: shared_preferences_web
+ sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.3"
+ shared_preferences_windows:
+ dependency: transitive
+ description:
+ name: shared_preferences_windows
+ sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.1"
sky_engine:
dependency: transitive
description: flutter
@@ -160,6 +533,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.12.1"
+ storage_client:
+ dependency: transitive
+ description:
+ name: storage_client
+ sha256: "1c61b19ed9e78f37fdd1ca8b729ab8484e6c8fe82e15c87e070b861951183657"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.1"
stream_channel:
dependency: transitive
description:
@@ -176,6 +557,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.1"
+ supabase:
+ dependency: transitive
+ description:
+ name: supabase
+ sha256: cc039f63a3168386b3a4f338f3bff342c860d415a3578f3fbe854024aee6f911
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.10.2"
+ supabase_flutter:
+ dependency: "direct main"
+ description:
+ name: supabase_flutter
+ sha256: "92b2416ecb6a5c3ed34cf6e382b35ce6cc8921b64f2a9299d5d28968d42b09bb"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.12.0"
term_glyph:
dependency: transitive
description:
@@ -192,6 +589,78 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.9"
+ typed_data:
+ dependency: transitive
+ description:
+ name: typed_data
+ sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.4.0"
+ url_launcher:
+ dependency: transitive
+ description:
+ name: url_launcher
+ sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.3.2"
+ url_launcher_android:
+ dependency: transitive
+ description:
+ name: url_launcher_android
+ sha256: "767344bf3063897b5cf0db830e94f904528e6dd50a6dfaf839f0abf509009611"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.3.28"
+ url_launcher_ios:
+ dependency: transitive
+ description:
+ name: url_launcher_ios
+ sha256: "580fe5dfb51671ae38191d316e027f6b76272b026370708c2d898799750a02b0"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.4.1"
+ url_launcher_linux:
+ dependency: transitive
+ description:
+ name: url_launcher_linux
+ sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.2.2"
+ url_launcher_macos:
+ dependency: transitive
+ description:
+ name: url_launcher_macos
+ sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.2.5"
+ url_launcher_platform_interface:
+ dependency: transitive
+ description:
+ name: url_launcher_platform_interface
+ sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.3.2"
+ url_launcher_web:
+ dependency: transitive
+ description:
+ name: url_launcher_web
+ sha256: d0412fcf4c6b31ecfdb7762359b7206ffba3bbffd396c6d9f9c4616ece476c1f
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.2"
+ url_launcher_windows:
+ dependency: transitive
+ description:
+ name: url_launcher_windows
+ sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.5"
vector_math:
dependency: transitive
description:
@@ -208,6 +677,54 @@ packages:
url: "https://pub.dev"
source: hosted
version: "15.0.2"
+ web:
+ dependency: transitive
+ description:
+ name: web
+ sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.1"
+ web_socket:
+ dependency: transitive
+ description:
+ name: web_socket
+ sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.1"
+ web_socket_channel:
+ dependency: transitive
+ description:
+ name: web_socket_channel
+ sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.3"
+ xdg_directories:
+ dependency: transitive
+ description:
+ name: xdg_directories
+ sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.0"
+ yaml:
+ dependency: transitive
+ description:
+ name: yaml
+ sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.3"
+ yet_another_json_isolate:
+ dependency: transitive
+ description:
+ name: yet_another_json_isolate
+ sha256: fe45897501fa156ccefbfb9359c9462ce5dec092f05e8a56109db30be864f01e
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.0"
sdks:
dart: ">=3.11.0 <4.0.0"
- flutter: ">=3.18.0-18.0.pre.54"
+ flutter: ">=3.38.4"
diff --git a/pubspec.yaml b/pubspec.yaml
index 1511f02..0cca785 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -34,6 +34,7 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
+ supabase_flutter: ^2.8.0
dev_dependencies:
flutter_test:
diff --git a/test/widget_test.dart b/test/widget_test.dart
index 08c4925..f485109 100644
--- a/test/widget_test.dart
+++ b/test/widget_test.dart
@@ -1,30 +1,9 @@
-// This is a basic Flutter widget test.
-//
-// To perform an interaction with a widget in your test, use the WidgetTester
-// utility in the flutter_test package. For example, you can send tap and scroll
-// gestures. You can also use WidgetTester to find child widgets in the widget
-// tree, read text, and verify that the values of widget properties are correct.
+// Basic smoke test placeholder — will be updated when UI is finalized.
-import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
-import 'package:hwhub/main.dart';
-
void main() {
- testWidgets('Counter increments smoke test', (WidgetTester tester) async {
- // Build our app and trigger a frame.
- await tester.pumpWidget(const MyApp());
-
- // Verify that our counter starts at 0.
- expect(find.text('0'), findsOneWidget);
- expect(find.text('1'), findsNothing);
-
- // Tap the '+' icon and trigger a frame.
- await tester.tap(find.byIcon(Icons.add));
- await tester.pump();
-
- // Verify that our counter has incremented.
- expect(find.text('0'), findsNothing);
- expect(find.text('1'), findsOneWidget);
+ test('placeholder test', () {
+ expect(1 + 1, 2);
});
}
diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc
index 8b6d468..785a046 100644
--- a/windows/flutter/generated_plugin_registrant.cc
+++ b/windows/flutter/generated_plugin_registrant.cc
@@ -6,6 +6,12 @@
#include "generated_plugin_registrant.h"
+#include
+#include
void RegisterPlugins(flutter::PluginRegistry* registry) {
+ AppLinksPluginCApiRegisterWithRegistrar(
+ registry->GetRegistrarForPlugin("AppLinksPluginCApi"));
+ UrlLauncherWindowsRegisterWithRegistrar(
+ registry->GetRegistrarForPlugin("UrlLauncherWindows"));
}
diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake
index b93c4c3..8f8ee4f 100644
--- a/windows/flutter/generated_plugins.cmake
+++ b/windows/flutter/generated_plugins.cmake
@@ -3,6 +3,8 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
+ app_links
+ url_launcher_windows
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST